From 7389250fcd1ada74514f90e8670d7af087e6a7c7 Mon Sep 17 00:00:00 2001 From: Mateusz Loskot Date: Wed, 6 May 2015 00:16:58 +0200 Subject: Add Boost.Build Project Manager plug-in Preparing to contribute the plug-in, see the announcement at http://lists.qt-project.org/pipermail/qt-creator/2015-February/004436.html Change-Id: Ic3920c9b888af5bea1b7742b8ff49984c29a2909 Reviewed-by: Orgad Shaneh Reviewed-by: Eike Ziller Reviewed-by: Leena Miettinen Reviewed-by: hjk --- .../BoostBuildProjectManager.json.in | 19 + .../BoostBuildProjectManager.mimetypes.xml | 36 + .../b2buildconfiguration.cpp | 391 +++++++++ .../b2buildconfiguration.h | 138 ++++ .../boostbuildprojectmanager/b2buildinfo.cpp | 26 + src/plugins/boostbuildprojectmanager/b2buildinfo.h | 44 ++ .../boostbuildprojectmanager/b2buildstep.cpp | 420 ++++++++++ src/plugins/boostbuildprojectmanager/b2buildstep.h | 162 ++++ .../b2openprojectwizard.cpp | 272 +++++++ .../boostbuildprojectmanager/b2openprojectwizard.h | 127 +++ .../boostbuildprojectmanager/b2outputparser.cpp | 197 +++++ .../boostbuildprojectmanager/b2outputparser.h | 64 ++ src/plugins/boostbuildprojectmanager/b2project.cpp | 306 +++++++ src/plugins/boostbuildprojectmanager/b2project.h | 106 +++ .../boostbuildprojectmanager/b2projectfile.cpp | 80 ++ .../boostbuildprojectmanager/b2projectfile.h | 47 ++ .../boostbuildprojectmanager/b2projectmanager.cpp | 64 ++ .../boostbuildprojectmanager/b2projectmanager.h | 54 ++ .../b2projectmanager_global.h | 23 + .../b2projectmanagerconstants.h | 68 ++ .../b2projectmanagerplugin.cpp | 83 ++ .../b2projectmanagerplugin.h | 48 ++ .../boostbuildprojectmanager/b2projectnode.cpp | 260 ++++++ .../boostbuildprojectmanager/b2projectnode.h | 77 ++ src/plugins/boostbuildprojectmanager/b2utility.cpp | 151 ++++ src/plugins/boostbuildprojectmanager/b2utility.h | 60 ++ .../boostbuildprojectmanager/boostbuildproject.qrc | 5 + .../boostbuildprojectmanager.pro | 46 ++ .../boostbuildprojectmanager_dependencies.pri | 12 + .../external/projectexplorer/clangparser.cpp | 270 +++++++ .../external/projectexplorer/clangparser.h | 59 ++ .../external/projectexplorer/gccparser.cpp | 880 +++++++++++++++++++++ .../external/projectexplorer/gccparser.h | 67 ++ .../external/projectexplorer/ldparser.cpp | 126 +++ .../external/projectexplorer/ldparser.h | 54 ++ .../filesselectionwizardpage.cpp | 232 ++++++ .../filesselectionwizardpage.h | 106 +++ .../selectablefilesmodel.cpp | 681 ++++++++++++++++ .../selectablefilesmodel.h | 189 +++++ src/plugins/plugins.pro | 1 + 40 files changed, 6051 insertions(+) create mode 100644 src/plugins/boostbuildprojectmanager/BoostBuildProjectManager.json.in create mode 100644 src/plugins/boostbuildprojectmanager/BoostBuildProjectManager.mimetypes.xml create mode 100644 src/plugins/boostbuildprojectmanager/b2buildconfiguration.cpp create mode 100644 src/plugins/boostbuildprojectmanager/b2buildconfiguration.h create mode 100644 src/plugins/boostbuildprojectmanager/b2buildinfo.cpp create mode 100644 src/plugins/boostbuildprojectmanager/b2buildinfo.h create mode 100644 src/plugins/boostbuildprojectmanager/b2buildstep.cpp create mode 100644 src/plugins/boostbuildprojectmanager/b2buildstep.h create mode 100644 src/plugins/boostbuildprojectmanager/b2openprojectwizard.cpp create mode 100644 src/plugins/boostbuildprojectmanager/b2openprojectwizard.h create mode 100644 src/plugins/boostbuildprojectmanager/b2outputparser.cpp create mode 100644 src/plugins/boostbuildprojectmanager/b2outputparser.h create mode 100644 src/plugins/boostbuildprojectmanager/b2project.cpp create mode 100644 src/plugins/boostbuildprojectmanager/b2project.h create mode 100644 src/plugins/boostbuildprojectmanager/b2projectfile.cpp create mode 100644 src/plugins/boostbuildprojectmanager/b2projectfile.h create mode 100644 src/plugins/boostbuildprojectmanager/b2projectmanager.cpp create mode 100644 src/plugins/boostbuildprojectmanager/b2projectmanager.h create mode 100644 src/plugins/boostbuildprojectmanager/b2projectmanager_global.h create mode 100644 src/plugins/boostbuildprojectmanager/b2projectmanagerconstants.h create mode 100644 src/plugins/boostbuildprojectmanager/b2projectmanagerplugin.cpp create mode 100644 src/plugins/boostbuildprojectmanager/b2projectmanagerplugin.h create mode 100644 src/plugins/boostbuildprojectmanager/b2projectnode.cpp create mode 100644 src/plugins/boostbuildprojectmanager/b2projectnode.h create mode 100644 src/plugins/boostbuildprojectmanager/b2utility.cpp create mode 100644 src/plugins/boostbuildprojectmanager/b2utility.h create mode 100644 src/plugins/boostbuildprojectmanager/boostbuildproject.qrc create mode 100644 src/plugins/boostbuildprojectmanager/boostbuildprojectmanager.pro create mode 100644 src/plugins/boostbuildprojectmanager/boostbuildprojectmanager_dependencies.pri create mode 100644 src/plugins/boostbuildprojectmanager/external/projectexplorer/clangparser.cpp create mode 100644 src/plugins/boostbuildprojectmanager/external/projectexplorer/clangparser.h create mode 100644 src/plugins/boostbuildprojectmanager/external/projectexplorer/gccparser.cpp create mode 100644 src/plugins/boostbuildprojectmanager/external/projectexplorer/gccparser.h create mode 100644 src/plugins/boostbuildprojectmanager/external/projectexplorer/ldparser.cpp create mode 100644 src/plugins/boostbuildprojectmanager/external/projectexplorer/ldparser.h create mode 100644 src/plugins/boostbuildprojectmanager/filesselectionwizardpage.cpp create mode 100644 src/plugins/boostbuildprojectmanager/filesselectionwizardpage.h create mode 100644 src/plugins/boostbuildprojectmanager/selectablefilesmodel.cpp create mode 100644 src/plugins/boostbuildprojectmanager/selectablefilesmodel.h diff --git a/src/plugins/boostbuildprojectmanager/BoostBuildProjectManager.json.in b/src/plugins/boostbuildprojectmanager/BoostBuildProjectManager.json.in new file mode 100644 index 0000000000..8700c63439 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/BoostBuildProjectManager.json.in @@ -0,0 +1,19 @@ +{ + \"Name\" : \"BoostBuildProjectManager\", + \"Version\" : \"$$QTCREATOR_VERSION\", + \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", + \"Vendor\" : \"Mateusz Łoskot\", + \"Copyright\" : \"2013 (C) Mateusz Łoskot\", + \"License\" : [ \"GNU Lesser General Public License, Version 2.1\", + \"\", + \"This is free software; you can redistribute and/or modify it under the terms\", + \"of the GNU Lesser General Public License, Version 2.1 as published\", + \"by the Free Software Foundation.\", + \"See accompanying fike LICENSE.txt file or\", + \"copy at http://www.gnu.org/licenses/lgpl-2.1-standalone.html for more information.\", + \"\" ], + \"Category\" : \"Build Systems\", + \"Description\" : \"Qt Creator plugin for Boost.Build\", + \"Url\" : \"https://github.com/mloskot/qt-creator-plugin-boostbuild/\", + $$dependencyList +} diff --git a/src/plugins/boostbuildprojectmanager/BoostBuildProjectManager.mimetypes.xml b/src/plugins/boostbuildprojectmanager/BoostBuildProjectManager.mimetypes.xml new file mode 100644 index 0000000000..e3391759bc --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/BoostBuildProjectManager.mimetypes.xml @@ -0,0 +1,36 @@ + + + + + + Boost.Build Project file + + + + + + + + + + + + + + Boost.Build Project Files + + + + + + Boost.Build Project Includes + + + + diff --git a/src/plugins/boostbuildprojectmanager/b2buildconfiguration.cpp b/src/plugins/boostbuildprojectmanager/b2buildconfiguration.cpp new file mode 100644 index 0000000000..796d868de0 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2buildconfiguration.cpp @@ -0,0 +1,391 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#include "b2buildconfiguration.h" +#include "b2buildinfo.h" +#include "b2buildstep.h" +#include "b2project.h" +#include "b2projectmanagerconstants.h" +#include "b2utility.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace ProjectExplorer; + +namespace BoostBuildProjectManager { +namespace Internal { + +BuildConfiguration::BuildConfiguration(Target* parent) + : ProjectExplorer::BuildConfiguration(parent, Core::Id(Constants::BUILDCONFIGURATION_ID)) +{ + ProjectExplorer::Project const* p = parent->project(); + Q_ASSERT(p); + setWorkingDirectory(p->projectDirectory()); +} + +BuildConfiguration::BuildConfiguration(Target* parent, BuildConfiguration* source) + : ProjectExplorer::BuildConfiguration(parent, source) +{ + if (BuildConfiguration* bc = qobject_cast(source)) + setWorkingDirectory(bc->workingDirectory()); +} + +BuildConfiguration::BuildConfiguration(Target* parent, Core::Id const id) + : ProjectExplorer::BuildConfiguration(parent, id) +{ + ProjectExplorer::Project const* p = parent->project(); + Q_ASSERT(p); + setWorkingDirectory(p->projectDirectory()); +} + +QVariantMap BuildConfiguration::toMap() const +{ + QVariantMap map(ProjectExplorer::BuildConfiguration::toMap()); + map.insert(QLatin1String(Constants::BC_KEY_WORKDIR), workingDirectory_.toString()); + return map; +} + +bool BuildConfiguration::fromMap(QVariantMap const& map) +{ + if (!ProjectExplorer::BuildConfiguration::fromMap(map)) + return false; + + QString dir = map.value(QLatin1String(Constants::BC_KEY_WORKDIR)).toString(); + setWorkingDirectory(Utils::FileName::fromString(dir)); + + return true; +} + +ProjectExplorer::NamedWidget* +BuildConfiguration::createConfigWidget() +{ + return new BuildSettingsWidget(this); +} + +BuildConfiguration::BuildType +BuildConfiguration::buildType() const +{ + BuildType type = Unknown; + + ProjectExplorer::BuildStepList* buildStepList + = stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD)); + Q_ASSERT(buildStepList); + foreach (ProjectExplorer::BuildStep* bs, buildStepList->steps()) { + if (BuildStep* bbStep = qobject_cast(bs)) { + type = bbStep->buildType(); + break; + } + } + + return type; +} + +Utils::FileName BuildConfiguration::workingDirectory() const +{ + Q_ASSERT(!workingDirectory_.isEmpty()); + return workingDirectory_; +} + +void BuildConfiguration::setWorkingDirectory(Utils::FileName const& dir) +{ + if (dir.isEmpty()) { + if (Target* t = target()) { + QString const dwd + = Project::defaultWorkingDirectory(t->project()->projectDirectory().toString()); + workingDirectory_ = Utils::FileName::fromString(dwd); + } + } else { + workingDirectory_ = dir; + } + + Q_ASSERT(!workingDirectory_.isEmpty()); + emitWorkingDirectoryChanged(); +} + +void BuildConfiguration::emitWorkingDirectoryChanged() +{ + if (workingDirectory() != lastEmmitedWorkingDirectory_) { + lastEmmitedWorkingDirectory_= workingDirectory(); + emit workingDirectoryChanged(); + } +} + +BuildConfigurationFactory::BuildConfigurationFactory(QObject* parent) + : IBuildConfigurationFactory(parent) +{ +} + +int +BuildConfigurationFactory::priority(ProjectExplorer::Target const* parent) const +{ + return canHandle(parent) ? 0 : -1; +} + +int +BuildConfigurationFactory::priority(ProjectExplorer::Kit const* k + , QString const& projectPath) const +{ + BBPM_QDEBUG(k->displayName() << ", " << projectPath); + + Utils::MimeDatabase mdb; + Utils::MimeType mimeType = mdb.mimeTypeForFile(QFileInfo(projectPath)); + + return (k && mimeType.matchesName(QLatin1String(Constants::MIMETYPE_JAMFILE))) + ? 0 + : -1; +} + +QList +BuildConfigurationFactory::availableBuilds(ProjectExplorer::Target const* parent) const +{ + BBPM_QDEBUG("target: " << parent->displayName()); + + ProjectExplorer::Project* project = parent->project(); + QString const projectPath(project->projectDirectory().toString()); + BBPM_QDEBUG(projectPath); + + QList result; + result << createBuildInfo(parent->kit(), projectPath, BuildConfiguration::Debug); + result << createBuildInfo(parent->kit(), projectPath, BuildConfiguration::Release); + return result; +} + +QList +BuildConfigurationFactory::availableSetups(ProjectExplorer::Kit const* k + , QString const& projectPath) const +{ + BBPM_QDEBUG(projectPath); + + QList result; + result << createBuildInfo(k, projectPath, BuildConfiguration::Debug); + result << createBuildInfo(k, projectPath, BuildConfiguration::Release); + return result; +} + +ProjectExplorer::BuildConfiguration* +BuildConfigurationFactory::create(ProjectExplorer::Target* parent + , ProjectExplorer::BuildInfo const* info) const +{ + QTC_ASSERT(parent, return 0); + QTC_ASSERT(info->factory() == this, return 0); + QTC_ASSERT(info->kitId == parent->kit()->id(), return 0); + QTC_ASSERT(!info->displayName.isEmpty(), return 0); + BBPM_QDEBUG(info->displayName); + + Project* project = qobject_cast(parent->project()); + QTC_ASSERT(project, return 0); + + BuildInfo const* bi = static_cast(info); + QScopedPointer bc(new BuildConfiguration(parent)); + bc->setDisplayName(bi->displayName); + bc->setDefaultDisplayName(bi->displayName); + bc->setBuildDirectory(bi->buildDirectory); + bc->setWorkingDirectory(bi->workingDirectory); + + BuildStepFactory* stepFactory = BuildStepFactory::getObject(); + QTC_ASSERT(stepFactory, return 0); + + // Build steps + if (ProjectExplorer::BuildStepList* buildSteps + = bc->stepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD)) { + BuildStep* step = stepFactory->create(buildSteps); + QTC_ASSERT(step, return 0); + step->setBuildType(bi->buildType); + buildSteps->insertStep(0, step); + } + + // Clean steps + if (ProjectExplorer::BuildStepList* cleanSteps + = bc->stepList(ProjectExplorer::Constants::BUILDSTEPS_CLEAN)) { + BuildStep* step = stepFactory->create(cleanSteps); + QTC_ASSERT(step, return 0); + step->setBuildType(bi->buildType); + cleanSteps->insertStep(0, step); + } + + return bc.take(); +} + +bool +BuildConfigurationFactory::canClone(ProjectExplorer::Target const* parent + , ProjectExplorer::BuildConfiguration* source) const +{ + Q_ASSERT(parent); + Q_ASSERT(source); + + return canHandle(parent) + ? source->id() == Constants::BUILDCONFIGURATION_ID + : false; +} + +BuildConfiguration* +BuildConfigurationFactory::clone(ProjectExplorer::Target* parent + , ProjectExplorer::BuildConfiguration* source) +{ + Q_ASSERT(parent); + Q_ASSERT(source); + + BuildConfiguration* copy = 0; + if (canClone(parent, source)) { + BuildConfiguration* old = static_cast(source); + copy = new BuildConfiguration(parent, old); + } + return copy; +} + +bool +BuildConfigurationFactory::canRestore(ProjectExplorer::Target const* parent + , QVariantMap const& map) const +{ + Q_ASSERT(parent); + + return canHandle(parent) + ? ProjectExplorer::idFromMap(map) == Constants::BUILDCONFIGURATION_ID + : false; +} + +BuildConfiguration* +BuildConfigurationFactory::restore(ProjectExplorer::Target *parent + , QVariantMap const& map) +{ + Q_ASSERT(parent); + + if (canRestore(parent, map)) { + QScopedPointer bc(new BuildConfiguration(parent)); + if (bc->fromMap(map)) + return bc.take(); + } + return 0; +} + +bool +BuildConfigurationFactory::canHandle(ProjectExplorer::Target const* t) const +{ + QTC_ASSERT(t, return false); + + return t->project()->supportsKit(t->kit()) + ? t->project()->id() == Constants::PROJECT_ID + : false; +} + +BuildInfo* +BuildConfigurationFactory::createBuildInfo(ProjectExplorer::Kit const* k + , QString const& projectPath + , BuildConfiguration::BuildType type) const +{ + Q_ASSERT(k); + + BuildInfo* info = new BuildInfo(this); + if (type == BuildConfiguration::Release) + info->displayName = tr("Release"); + else + info->displayName = tr("Debug"); + info->typeName = tr("Default (%1)").arg(info->displayName); + info->buildType = type; + info->buildDirectory = defaultBuildDirectory(projectPath); + info->workingDirectory = defaultWorkingDirectory(projectPath); + info->kitId = k->id(); + + BBPM_QDEBUG(info->typeName << " in " << projectPath); + return info; +} + +/*static*/ Utils::FileName +BuildConfigurationFactory::defaultBuildDirectory(QString const& top) +{ + return Utils::FileName::fromString(Project::defaultBuildDirectory(top)); +} + +/*static*/ Utils::FileName +BuildConfigurationFactory::defaultWorkingDirectory(QString const& top) +{ + return Utils::FileName::fromString(Project::defaultWorkingDirectory(top)); +} + +BuildSettingsWidget::BuildSettingsWidget(BuildConfiguration* bc) + : bc_(bc) + , buildPathChooser_(0) +{ + setDisplayName(tr("Boost.Build Manager")); + + QFormLayout* fl = new QFormLayout(this); + fl->setContentsMargins(0, -1, 0, -1); + fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); + + QString const projectPath(bc_->target()->project()->projectDirectory().toString()); + + // Working directory + workPathChooser_ = new Utils::PathChooser(this); + workPathChooser_->setEnabled(true); + workPathChooser_->setEnvironment(bc_->environment()); + workPathChooser_->setBaseDirectory(projectPath); + workPathChooser_->setPath(bc_->workingDirectory().toString()); + fl->addRow(tr("Run Boost.Build in:"), workPathChooser_); + + // Build directory + buildPathChooser_ = new Utils::PathChooser(this); + buildPathChooser_->setEnabled(true); + buildPathChooser_->setEnvironment(bc_->environment()); + buildPathChooser_->setBaseDirectory(projectPath); + buildPathChooser_->setPath(bc_->rawBuildDirectory().toString()); + fl->addRow(tr("Set build directory to:"), buildPathChooser_); + + connect(workPathChooser_, SIGNAL(changed(QString)) + , this, SLOT(workingDirectoryChanged())); + + connect(buildPathChooser_, SIGNAL(changed(QString)) + , this, SLOT(buildDirectoryChanged())); + + connect(bc, SIGNAL(environmentChanged()) + , this, SLOT(environmentHasChanged())); +} + +void BuildSettingsWidget::buildDirectoryChanged() +{ + QTC_ASSERT(bc_, return); + bc_->setBuildDirectory(Utils::FileName::fromString(buildPathChooser_->rawPath())); +} + +void BuildSettingsWidget::workingDirectoryChanged() +{ + QTC_ASSERT(bc_, return); + bc_->setWorkingDirectory(Utils::FileName::fromString(workPathChooser_->rawPath())); +} + +void BuildSettingsWidget::environmentHasChanged() +{ + Q_ASSERT(buildPathChooser_); + buildPathChooser_->setEnvironment(bc_->environment()); +} + +} // namespace Internal +} // namespace BoostBuildProjectManager diff --git a/src/plugins/boostbuildprojectmanager/b2buildconfiguration.h b/src/plugins/boostbuildprojectmanager/b2buildconfiguration.h new file mode 100644 index 0000000000..a9edb7743e --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2buildconfiguration.h @@ -0,0 +1,138 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBBUILDCONFIGURATION_HPP +#define BBBUILDCONFIGURATION_HPP + +// Qt Creator +#include +#include +#include +// Qt +#include +#include +#include + +namespace Utils { +class FileName; +class PathChooser; +} + +namespace BoostBuildProjectManager { +namespace Internal { + +class BuildInfo; +class BuildSettingsWidget; + +class BuildConfiguration : public ProjectExplorer::BuildConfiguration +{ + Q_OBJECT + + friend class BuildConfigurationFactory; + +public: + explicit BuildConfiguration(ProjectExplorer::Target* parent); + + QVariantMap toMap() const; + + ProjectExplorer::NamedWidget* createConfigWidget(); + + BuildType buildType() const; + + Utils::FileName workingDirectory() const; + void setWorkingDirectory(Utils::FileName const& dir); + +signals: + void workingDirectoryChanged(); + +protected: + BuildConfiguration(ProjectExplorer::Target* parent, BuildConfiguration* source); + BuildConfiguration(ProjectExplorer::Target* parent, Core::Id const id); + + bool fromMap(QVariantMap const& map); + + friend class BuildSettingsWidget; + +private slots: + void emitWorkingDirectoryChanged(); + +private: + Utils::FileName workingDirectory_; + Utils::FileName lastEmmitedWorkingDirectory_; +}; + +class BuildConfigurationFactory : public ProjectExplorer::IBuildConfigurationFactory +{ + Q_OBJECT + +public: + explicit BuildConfigurationFactory(QObject* parent = 0); + + int priority(ProjectExplorer::Target const* parent) const; + int priority(ProjectExplorer::Kit const* k, QString const& projectPath) const; + + QList + availableBuilds(ProjectExplorer::Target const* parent) const; + + QList + availableSetups(ProjectExplorer::Kit const* k, QString const& projectPath) const; + + ProjectExplorer::BuildConfiguration* + create(ProjectExplorer::Target* parent + , ProjectExplorer::BuildInfo const* info) const; + + bool + canClone(ProjectExplorer::Target const* parent + , ProjectExplorer::BuildConfiguration* source) const; + + BuildConfiguration* + clone(ProjectExplorer::Target* parent, ProjectExplorer::BuildConfiguration* source); + + bool + canRestore(ProjectExplorer::Target const* parent, QVariantMap const& map) const; + + BuildConfiguration* + restore(ProjectExplorer::Target *parent, QVariantMap const& map); + + static Utils::FileName defaultBuildDirectory(QString const& top); + static Utils::FileName defaultWorkingDirectory(QString const& top); + +private: + bool canHandle(ProjectExplorer::Target const* target) const; + + BuildInfo* + createBuildInfo(ProjectExplorer::Kit const* k + , QString const& projectPath + , BuildConfiguration::BuildType type) const; +}; + +class BuildSettingsWidget : public ProjectExplorer::NamedWidget +{ + Q_OBJECT + +public: + BuildSettingsWidget(BuildConfiguration* bc); + +private slots: + void environmentHasChanged(); + void buildDirectoryChanged(); + void workingDirectoryChanged(); + +private: + BuildConfiguration* bc_; + Utils::PathChooser* workPathChooser_; + Utils::PathChooser* buildPathChooser_; +}; + +} // namespace Internal +} // namespace BoostBuildProjectManager + +#endif // BBBUILDCONFIGURATION_HPP diff --git a/src/plugins/boostbuildprojectmanager/b2buildinfo.cpp b/src/plugins/boostbuildprojectmanager/b2buildinfo.cpp new file mode 100644 index 0000000000..927a7ab909 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2buildinfo.cpp @@ -0,0 +1,26 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#include "b2buildinfo.h" +#include "b2buildconfiguration.h" +#include "b2projectmanagerconstants.h" + +namespace BoostBuildProjectManager { +namespace Internal { + +BuildInfo::BuildInfo(BuildConfigurationFactory const* f) + : ProjectExplorer::BuildInfo(f) + , buildType(BuildConfiguration::Debug) +{ +} + +} // namespace Internal +} // namespace BoostBuildProjectManager diff --git a/src/plugins/boostbuildprojectmanager/b2buildinfo.h b/src/plugins/boostbuildprojectmanager/b2buildinfo.h new file mode 100644 index 0000000000..30374592ee --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2buildinfo.h @@ -0,0 +1,44 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBBUILDINFO_HPP +#define BBBUILDINFO_HPP + +#include "b2buildconfiguration.h" +// Qt Creator +#include +#include +#include +// Qt + +namespace BoostBuildProjectManager { +namespace Internal { + +class BuildConfigurationFactory; + +class BuildInfo : public ProjectExplorer::BuildInfo +{ +public: + explicit BuildInfo(BuildConfigurationFactory const* f); + + // Boost.Build option variant={debug|release} + // By default, the debug variant is set. + BuildConfiguration::BuildType buildType; + + // Boost.Build command working directory. + // By default, empty what indicates project path from where Jamfile was opened. + Utils::FileName workingDirectory; +}; + +} // namespace Internal +} // namespace BoostBuildProjectManager + +#endif // BBBUILDINFO_HPP diff --git a/src/plugins/boostbuildprojectmanager/b2buildstep.cpp b/src/plugins/boostbuildprojectmanager/b2buildstep.cpp new file mode 100644 index 0000000000..d6bc0772a7 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2buildstep.cpp @@ -0,0 +1,420 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// Copyright (C) 2013 Openismus GmbH. +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#include "b2buildconfiguration.h" +#include "b2buildstep.h" +#include "b2outputparser.h" +#include "b2projectmanagerconstants.h" +#include "b2utility.h" +// Qt Creator +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// Qt +#include +#include +#include +#include +// std +#include + +namespace BoostBuildProjectManager { +namespace Internal { + +BuildStep::BuildStep(ProjectExplorer::BuildStepList* bsl) + : ProjectExplorer::AbstractProcessStep(bsl, Core::Id(Constants::BUILDSTEP_ID)) +{ +} + +BuildStep::BuildStep(ProjectExplorer::BuildStepList* bsl, BuildStep* bs) + : AbstractProcessStep(bsl, bs) + , tasks_(bs->tasks_) + , arguments_(bs->arguments_) +{ +} + +BuildStep::BuildStep(ProjectExplorer::BuildStepList* bsl, Core::Id const id) + : AbstractProcessStep(bsl, id) +{ +} + +bool BuildStep::init() +{ + setDefaultDisplayName(QLatin1String(Constants::BOOSTBUILD)); + + tasks_.clear(); + ProjectExplorer::ToolChain* tc + = ProjectExplorer::ToolChainKitInformation::toolChain(target()->kit()); + if (!tc) { + BBPM_QDEBUG("Qt Creator needs compiler"); + typedef ProjectExplorer::Task Task; + Task task(Task::Error + , tr("Qt Creator needs a compiler set up to build. Configure a compiler in the kit options.") + , Utils::FileName(), -1 + , ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); + + tasks_.append(task); + return !tasks_.empty(); // otherwise the tasks will not get reported + } + + BuildConfiguration* bc = thisBuildConfiguration(); + QTC_ASSERT(bc, return false); + + setIgnoreReturnValue(Constants::ReturnValueNotIgnored); + + ProjectExplorer::ProcessParameters* pp = processParameters(); + pp->setMacroExpander(bc->macroExpander()); + { + // from GenericProjectManager code: + // Force output to english for the parsers. + // Do this here and not in the toolchain's addToEnvironment() to not + // screw up the users run environment. + Utils::Environment env = bc->environment(); + env.set(QLatin1String("LC_ALL"), QLatin1String("C")); + pp->setEnvironment(env); + } + pp->setWorkingDirectory(bc->workingDirectory().toString()); + pp->setCommand(makeCommand(bc->environment())); + pp->setArguments(allArguments()); + pp->resolveAll(); + + // Create Boost.Build parser and chain with existing parsers + setOutputParser(new BoostBuildParser()); + if (ProjectExplorer::IOutputParser* parser = target()->kit()->createOutputParser()) + appendOutputParser(parser); + outputParser()->setWorkingDirectory(pp->effectiveWorkingDirectory()); + + BBPM_QDEBUG(displayName() << ", " << bc->buildDirectory().toString() + << ", " << pp->effectiveWorkingDirectory()); + + return ProjectExplorer::AbstractProcessStep::init(); +} + +void BuildStep::run(QFutureInterface& fi) +{ + BBPM_QDEBUG("running: " << displayName()); + + bool canContinue = true; + foreach (ProjectExplorer::Task const& t, tasks_) { + addTask(t); + canContinue = false; + } + + if (!canContinue) { + emit addOutput(tr("Configuration is faulty. Check the Issues view for details.") + , BuildStep::MessageOutput); + fi.reportResult(false); + emit finished(); + } else { + AbstractProcessStep::run(fi); + } +} + +BuildConfiguration* BuildStep::thisBuildConfiguration() const +{ + BuildConfiguration* bc = 0; + if (ProjectExplorer::BuildConfiguration* bcBase = buildConfiguration()) { + bc = qobject_cast(bcBase); + } else { + // TODO: Do we need to do anything with this case? + // From QmakeProjectManager: + // That means the step is in the deploylist, so we listen to the active build + // config changed signal and react... + + bcBase = target()->activeBuildConfiguration(); + bc = qobject_cast(bcBase); + } + return bc; +} + +ProjectExplorer::BuildStepConfigWidget* BuildStep::createConfigWidget() +{ + return new BuildStepConfigWidget(this); +} + +bool BuildStep::immutable() const +{ + return false; +} + +QVariantMap BuildStep::toMap() const +{ + QVariantMap map(ProjectExplorer::AbstractProcessStep::toMap()); + map.insert(QLatin1String(Constants::BS_KEY_ARGUMENTS), additionalArguments()); + return map; +} + +bool BuildStep::fromMap(QVariantMap const& map) +{ + QString const args(map.value(QLatin1String(Constants::BS_KEY_ARGUMENTS)).toString()); + setAdditionalArguments(args); + + return ProjectExplorer::AbstractProcessStep::fromMap(map); +} + +QString BuildStep::makeCommand(Utils::Environment const& env) const +{ + Q_UNUSED(env); + return QLatin1String(Constants::COMMAND_BB2); +} + +QString BuildStep::additionalArguments() const +{ + return Utils::QtcProcess::joinArgs(arguments_); +} + +QString BuildStep::allArguments() const +{ + QStringList args(arguments_); + + // Collect implicit arguments not specified by user directly as option=value pair + if (ProjectExplorer::BuildConfiguration* bc = buildConfiguration()) { + QString const builddir(bc->buildDirectory().toString()); + if (!builddir.isEmpty()) + args.append(QLatin1String("--build-dir=") + builddir); + } + + return Utils::QtcProcess::joinArgs(args); +} + +void BuildStep::appendAdditionalArgument(QString const& arg) +{ + // TODO: use map or find duplicates? + arguments_.append(arg); +} + +void BuildStep::setAdditionalArguments(QString const& args) +{ + Utils::QtcProcess::SplitError err; + QStringList argsList = Utils::QtcProcess::splitArgs(args, Utils::HostOsInfo::hostOs(), false, &err); + if (err == Utils::QtcProcess::SplitOk) { + arguments_ = argsList; + emit argumentsChanged(args); + } +} + +ProjectExplorer::BuildConfiguration::BuildType +BuildStep::buildType() const +{ + // TODO: what is user inputs "variant = release" or mixed-case value? + return arguments_.contains(QLatin1String("variant=release")) + ? BuildConfiguration::Release + : BuildConfiguration::Debug; +} + +void +BuildStep::setBuildType(ProjectExplorer::BuildConfiguration::BuildType type) +{ + // TODO: Move literals to constants + QString arg(QLatin1String("variant=")); + if (type == BuildConfiguration::Release) + arg += QLatin1String("release"); + else + arg += QLatin1String("debug"); + + appendAdditionalArgument(arg); +} + +BuildStepFactory::BuildStepFactory(QObject* parent) + : IBuildStepFactory(parent) +{ +} + +/*static*/ BuildStepFactory* BuildStepFactory::getObject() +{ + return ExtensionSystem::PluginManager::getObject(); +} + +QList +BuildStepFactory::availableCreationIds(ProjectExplorer::BuildStepList* parent) const +{ + return canHandle(parent) + ? QList() << Core::Id(Constants::BUILDSTEP_ID) + : QList(); +} + +QString BuildStepFactory::displayNameForId(Core::Id const id) const +{ + BBPM_QDEBUG("id: " << id.toString()); + + QString name; + if (id == Constants::BUILDSTEP_ID) { + name = tr("Boost.Build" + , "Display name for BoostBuildProjectManager::BuildStep id."); + } + return name; +} + +bool BuildStepFactory::canCreate(ProjectExplorer::BuildStepList* parent + , Core::Id const id) const +{ + return canHandle(parent) && Core::Id(Constants::BUILDSTEP_ID) == id; +} + +BuildStep* +BuildStepFactory::create(ProjectExplorer::BuildStepList* parent) +{ + ProjectExplorer::BuildStep* step = create(parent, Constants::BUILDSTEP_ID); + return qobject_cast(step); +} + +ProjectExplorer::BuildStep* +BuildStepFactory::create(ProjectExplorer::BuildStepList* parent, Core::Id const id) +{ + BBPM_QDEBUG("id: " << id.toString()); + if (!canCreate(parent, id)) + return 0; + + BuildStep* step = new BuildStep(parent); + + if (parent->id() == ProjectExplorer::Constants::BUILDSTEPS_CLEAN) + step->appendAdditionalArgument(QLatin1String("--clean")); + + return step; +} + +bool BuildStepFactory::canClone(ProjectExplorer::BuildStepList *parent + , ProjectExplorer::BuildStep *source) const +{ + return canCreate(parent, source->id()); +} + +ProjectExplorer::BuildStep* +BuildStepFactory::clone(ProjectExplorer::BuildStepList* parent + , ProjectExplorer::BuildStep* source) +{ + return canClone(parent, source) + ? new BuildStep(parent, static_cast(source)) + : 0; +} + +bool BuildStepFactory::canRestore(ProjectExplorer::BuildStepList* parent + , QVariantMap const& map) const +{ + return canCreate(parent, ProjectExplorer::idFromMap(map)); +} + +ProjectExplorer::BuildStep* +BuildStepFactory::restore(ProjectExplorer::BuildStepList* parent + , QVariantMap const& map) +{ + Q_ASSERT(parent); + + if (canRestore(parent, map)) { + QScopedPointer bs(new BuildStep(parent)); + if (bs->fromMap(map)) + return bs.take(); + } + return 0; +} + +bool BuildStepFactory::canHandle(ProjectExplorer::BuildStepList* parent) const +{ + QTC_ASSERT(parent, return false); + return parent->target()->project()->id() == Constants::PROJECT_ID; +} + +BuildStepConfigWidget::BuildStepConfigWidget(BuildStep* step) + : step_(step) +{ + QFormLayout *fl = new QFormLayout(this); + fl->setMargin(0); + fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); + setLayout(fl); + + arguments_ = new QLineEdit(this); + arguments_->setText(step_->additionalArguments()); + fl->addRow(tr("Arguments:"), arguments_); + + updateDetails(); + + connect(arguments_, SIGNAL(textChanged(QString)) + , step, SLOT(setAdditionalArguments(QString))); + connect(step, SIGNAL(argumentsChanged(QString)) + , this, SLOT(updateDetails())); + connect(step_->project(), SIGNAL(environmentChanged()) + , this, SLOT(updateDetails())); + + if (BuildConfiguration* bc = step_->thisBuildConfiguration()) { + connect(bc, SIGNAL(buildDirectoryChanged()) + , this, SLOT(updateDetails())); + } +} + +BuildStepConfigWidget::~BuildStepConfigWidget() +{ +} + +QString BuildStepConfigWidget::displayName() const +{ + return tr(Constants::BOOSTBUILD + , "BoostBuildProjectManager::BuildStep display name."); +} + +QString BuildStepConfigWidget::summaryText() const +{ + return summaryText_; +} + +void BuildStepConfigWidget::setSummaryText(QString const& text) +{ + if (text != summaryText_) { + summaryText_ = text; + emit updateSummary(); + } +} + +void BuildStepConfigWidget::updateDetails() +{ + ProjectExplorer::ToolChain* tc + = ProjectExplorer::ToolChainKitInformation::toolChain(step_->target()->kit()); + if (!tc) { + setSummaryText(tr("%1: %2") + .arg(QLatin1String(Constants::BOOSTBUILD)) + .arg(ProjectExplorer::ToolChainKitInformation::msgNoToolChainInTarget())); + return; + } + + BuildConfiguration* bc = step_->thisBuildConfiguration(); + QTC_ASSERT(bc, return;); + + ProjectExplorer::ProcessParameters params; + params.setMacroExpander(bc->macroExpander()); + params.setEnvironment(bc->environment()); + params.setWorkingDirectory(bc->workingDirectory().toString()); + params.setCommand(step_->makeCommand(bc->environment())); + params.setArguments(step_->allArguments()); + + if (params.commandMissing()) { + setSummaryText(tr("%1: %2 not found in the environment.") + .arg(QLatin1String(Constants::BOOSTBUILD)) + .arg(params.command())); // Override display text + } else { + setSummaryText(params.summary(displayName())); + } + +} + +} // namespace Internal +} // namespace BoostBuildProjectManager diff --git a/src/plugins/boostbuildprojectmanager/b2buildstep.h b/src/plugins/boostbuildprojectmanager/b2buildstep.h new file mode 100644 index 0000000000..3b587777d2 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2buildstep.h @@ -0,0 +1,162 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBBUILDSTEP_HPP +#define BBBUILDSTEP_HPP + +// Qt Creator +#include +#include +#include +#include +// Qt +#include +#include +#include +#include + +namespace ProjectExplorer { +class Project; +} + +namespace Utils { +class Environment; +} + +namespace BoostBuildProjectManager { +namespace Internal { + +class BuildConfiguration; + +class BuildStep : public ProjectExplorer::AbstractProcessStep +{ + Q_OBJECT + friend class BuildStepFactory; + friend class BuildStepConfigWidget; + +public: + + explicit BuildStep(ProjectExplorer::BuildStepList* bsl); + + bool init(); + + void run(QFutureInterface& interface); + + ProjectExplorer::BuildStepConfigWidget* createConfigWidget(); + + bool immutable() const; + + QVariantMap toMap() const; + + bool fromMap(QVariantMap const& map); + + QString makeCommand(Utils::Environment const& env) const; + + QString additionalArguments() const; + + QString allArguments() const; + + void appendAdditionalArgument(QString const& arg); + + ProjectExplorer::BuildConfiguration::BuildType + buildType() const; + + void setBuildType(ProjectExplorer::BuildConfiguration::BuildType type); + +public slots: + void setAdditionalArguments(QString const& list); + +signals: + void argumentsChanged(QString const& list); + +protected: + BuildStep(ProjectExplorer::BuildStepList* bsl, BuildStep* bs); + BuildStep(ProjectExplorer::BuildStepList* bsl, Core::Id const id); + +private: + BuildConfiguration* thisBuildConfiguration() const; + + QList tasks_; + QStringList arguments_; +}; + +// Factory used to create instances of BuildStep. +class BuildStepFactory : public ProjectExplorer::IBuildStepFactory +{ + Q_OBJECT + +public: + BuildStepFactory(QObject* parent = 0); + + static BuildStepFactory* getObject(); + + QList + availableCreationIds(ProjectExplorer::BuildStepList* bc) const; + + QString + displayNameForId(const Core::Id id) const; + + bool + canCreate(ProjectExplorer::BuildStepList* parent, Core::Id const id) const; + + BuildStep* + create(ProjectExplorer::BuildStepList* parent); + + ProjectExplorer::BuildStep* + create(ProjectExplorer::BuildStepList* parent, Core::Id const id); + + bool + canClone(ProjectExplorer::BuildStepList *parent + , ProjectExplorer::BuildStep *source) const; + + ProjectExplorer::BuildStep* + clone(ProjectExplorer::BuildStepList* parent, ProjectExplorer::BuildStep* source); + + bool + canRestore(ProjectExplorer::BuildStepList* parent, QVariantMap const& map) const; + + ProjectExplorer::BuildStep* + restore(ProjectExplorer::BuildStepList* parent, QVariantMap const& map); + +private: + + bool + canHandle(ProjectExplorer::BuildStepList* parent) const; +}; + +class BuildStepConfigWidget : public ProjectExplorer::BuildStepConfigWidget +{ + Q_OBJECT + +public: + explicit BuildStepConfigWidget(BuildStep* buildStep); + ~BuildStepConfigWidget(); + QString displayName() const; + QString summaryText() const; + +private slots: + + void updateDetails(); + +private: + void setSummaryText(const QString &text); + + ProjectExplorer::BuildConfiguration* bc_; + BuildStep* step_; + QString summaryText_; + QLineEdit* arguments_; +}; + +} // namespace Internal +} // namespace BoostBuildProjectManager + +#endif // BBBUILDSTEP_HPP + diff --git a/src/plugins/boostbuildprojectmanager/b2openprojectwizard.cpp b/src/plugins/boostbuildprojectmanager/b2openprojectwizard.cpp new file mode 100644 index 0000000000..d69ddc1385 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2openprojectwizard.cpp @@ -0,0 +1,272 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#include "b2openprojectwizard.h" +#include "b2project.h" +#include "b2projectmanagerconstants.h" +#include "b2utility.h" +#include "filesselectionwizardpage.h" +// Qt Creator +#include +#include +#include +#include +#include +#include +#include +// Qt +#include +#include +#include +#include +#include +#include + +namespace BoostBuildProjectManager { +namespace Internal { + +////////////////////////////////////////////////////////////////////////////////////////// + +OpenProjectWizard::OpenProjectWizard(Project const* const project) + : project_(project) + , projectOpened_(false) +{ + // Project instance has been created, but it's only partially initialised and + // rest of the initialization takes place after this wizard completes. + Q_ASSERT(project_); + + setDisplayName(tr("Open %1 Project").arg(BBPM_C(BOOSTBUILD))); + setId(BBPM_C(PROJECT_WIZARD_ID)); + setWizardKind(ProjectWizard); // affects dir vs file path and sub-projects handling + + // TODO: do we need categories or flags? +} + +bool OpenProjectWizard::run(QString const& platform, QVariantMap const& extraValues) +{ + QVariantMap extraValuesCopy(extraValues); + + // Project name should be passed by caller, but, + // since we have Project instance handy, double-check. + if (!extraValuesCopy.contains(BBPM_C(P_KEY_PROJECTNAME))) + extraValuesCopy.insert(BBPM_C(P_KEY_PROJECTNAME), project_->displayName()); + + projectOpened_ = false; + outputValues_.clear(); + + runWizard(project_->projectFilePath().toString(), 0, platform, extraValuesCopy); + + return projectOpened_; +} + +Core::BaseFileWizard *OpenProjectWizard::create(QWidget* parent, Core::WizardDialogParameters const& parameters) const +{ + OpenProjectWizardDialog* wizard(new OpenProjectWizardDialog(parent + , parameters.defaultPath(), parameters.extraValues() + , const_cast(this)->outputValues_)); + + foreach (QWizardPage* p, parameters.extensionPages()) + wizard->addPage(p); + + return wizard; +} + +Core::GeneratedFiles +OpenProjectWizard::generateFiles(QWizard const* wizard, QString* errorMessage) const +{ + Q_UNUSED(errorMessage) + Q_ASSERT(project_); + + OpenProjectWizardDialog const* openWizard + = qobject_cast(wizard); + + QDir const projectDir(openWizard->path()); + + // Set up MIME filters for C/C++ headers + QStringList headerFilters; + QStringList const headerMimeTypes = QStringList() + << QLatin1String("text/x-chdr") << QLatin1String("text/x-c++hdr"); + + Utils::MimeDatabase mdb; + foreach (QString const& headerMime, headerMimeTypes) { + Utils::MimeType mime = mdb.mimeTypeForName(headerMime); + foreach (QString const& gp, mime.globPatterns()) + headerFilters.append(gp); + } + + // Generate list of include paths. + // If any C/C++ headers in any directory from paths, add it to include paths, + // used for C/C++ parsing only. + QStringList includePaths; + QStringList const paths = openWizard->selectedPaths(); + foreach (QString const& path, paths) { + QFileInfo const fileInfo(path); + QDir const thisDir(fileInfo.absoluteFilePath()); + if (!thisDir.entryList(headerFilters, QDir::Files).isEmpty()) { + QString const relative = projectDir.relativeFilePath(thisDir.path()); + includePaths.append(relative.isEmpty() ? QLatin1String(".") : relative); + } + } + + // Generate list of sources + QStringList sources = openWizard->selectedFiles(); + Utility::makeRelativePaths(projectDir.absolutePath(), sources); + + Core::GeneratedFile generatedFilesFile(project_->filesFilePath()); + generatedFilesFile.setContents(sources.join(QLatin1String("\n"))); + + Core::GeneratedFile generatedIncludesFile(project_->includesFilePath()); + generatedIncludesFile.setContents(includePaths.join(QLatin1String("\n"))); + + Core::GeneratedFiles files; + files.append(generatedFilesFile); + files.append(generatedIncludesFile); + return files; +} + +bool +OpenProjectWizard::postGenerateFiles(QWizard const* wizard + , Core::GeneratedFiles const& files, QString* errorMessage) +{ + Q_UNUSED(wizard); + + projectOpened_ + = ProjectExplorer::CustomProjectWizard::postGenerateOpen(files, errorMessage); + + return projectOpened_; +} + +////////////////////////////////////////////////////////////////////////////////////////// +OpenProjectWizardDialog::OpenProjectWizardDialog(QWidget* parent + , QString const& projectFile + , QVariantMap const& extraValues, QVariantMap& outputValues) + : Core::BaseFileWizard(parent) + , outputValues_(outputValues) + , extraValues_(extraValues) + , projectFile_(projectFile) +{ + setWindowTitle(tr("Open %1 Project").arg(BBPM_C(BOOSTBUILD))); + + pathsPage_ = new PathsSelectionWizardPage(this); + pathsPage_->setTitle(tr("Project Name and Paths")); + int const pathsPageId = addPage(pathsPage_); + wizardProgress()->item(pathsPageId)->setTitle(tr("Location")); + + filesPage_ = new FilesSelectionWizardPage(this); + filesPage_->setTitle(tr("File Selection")); + int const filesPageId = addPage(filesPage_); + wizardProgress()->item(filesPageId)->setTitle(tr("Files")); +} + + +QString OpenProjectWizardDialog::path() const +{ + QFileInfo const projectFileInfo(projectFile()); + QTC_ASSERT(projectFileInfo.isFile(), return QString()); + + return projectFileInfo.absoluteDir().absolutePath(); +} + +QString OpenProjectWizardDialog::projectFile() const +{ + return projectFile_; +} + +QString OpenProjectWizardDialog::projectName() const +{ + return pathsPage_->projectName(); +} + +QString OpenProjectWizardDialog::defaultProjectName() const +{ + return extraValues_.value(BBPM_C(P_KEY_PROJECTNAME)).toString(); +} + +QStringList OpenProjectWizardDialog::selectedFiles() const +{ + return filesPage_->selectedFiles(); +} + +QStringList OpenProjectWizardDialog::selectedPaths() const +{ + return filesPage_->selectedPaths(); +} + +void OpenProjectWizardDialog::setProjectName(QString const& name) +{ + outputValues_.insert(QLatin1String(Constants::P_KEY_PROJECTNAME), name); +} + +////////////////////////////////////////////////////////////////////////////////////////// +PathsSelectionWizardPage::PathsSelectionWizardPage(OpenProjectWizardDialog* wizard) + : QWizardPage(wizard) + , wizard_(wizard) +{ + QFormLayout *fl = new QFormLayout(); + setLayout(fl); + + QLabel* pathLabel = new QLabel(this); + pathLabel->setText(tr("Opening the following Jamfile as a project:")); + fl->addRow(pathLabel); + + QLineEdit* pathLine = new QLineEdit(this); + pathLine->setReadOnly(true); + pathLine->setDisabled(true); + pathLine->setText(wizard_->projectFile()); + fl->addRow(pathLine); + + QString projectName(Utility::parseJamfileProjectName(wizard_->projectFile())); + if (projectName.isEmpty()) + projectName = wizard_->defaultProjectName(); + + nameLineEdit_ = new QLineEdit(this); + connect(nameLineEdit_, &QLineEdit::textChanged + , wizard_, &OpenProjectWizardDialog::setProjectName); + nameLineEdit_->setText(projectName); + fl->addRow(tr("Project name:"), nameLineEdit_); + + QLabel* defaultsLabel = new QLabel(this); + defaultsLabel->setText(tr("Default Boost.Build runtime locations:")); + fl->addRow(defaultsLabel); + + QLineEdit* workingLine = new QLineEdit(this); + workingLine->setReadOnly(true); + workingLine->setDisabled(true); + workingLine->setText(Project::defaultWorkingDirectory(wizard_->projectFile())); + fl->addRow(tr("Working directory:"), workingLine); + + QLineEdit* buildLine = new QLineEdit(this); + buildLine->setReadOnly(true); + buildLine->setDisabled(true); + buildLine->setText(Project::defaultBuildDirectory(wizard_->projectFile())); + fl->addRow(tr("Build directory:"), buildLine); + + // TODO: indicate if we can find Boost.Build executable? + + QString const info(tr( + "This allows you to use Qt Creator as an IDE to edit and navigate C++ code, " + "run %1 command, work through compilation issues, " + "configure executable targets to run and debug.") + .arg(QLatin1String(Constants::BOOSTBUILD))); + QLabel* infoLabel = new QLabel(this); + infoLabel->setWordWrap(true); + infoLabel->setText(info); + fl->addRow(infoLabel); +} + +QString PathsSelectionWizardPage::projectName() const +{ + return nameLineEdit_->text(); +} + +} // namespace Internal +} // namespace BoostBuildProjectManager diff --git a/src/plugins/boostbuildprojectmanager/b2openprojectwizard.h b/src/plugins/boostbuildprojectmanager/b2openprojectwizard.h new file mode 100644 index 0000000000..d7d6a93f63 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2openprojectwizard.h @@ -0,0 +1,127 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBOPENPROJECTWIZARD_HPP +#define BBOPENPROJECTWIZARD_HPP + +// Qt Creator +#include +#include +#include +#include +// Qt +#include +#include +QT_BEGIN_NAMESPACE +class QVBoxLayout; +class QLabel; +class QTreeView; +class QLineEdit; +QT_END_NAMESPACE + +namespace Utils { +class PathChooser; +} + +namespace BoostBuildProjectManager { +namespace Internal { + +class Project; +class PathsSelectionWizardPage; +class FilesSelectionWizardPage; + +////////////////////////////////////////////////////////////////////////////////////////// +// NOTE: The Boost.Build wizard is based on Core::BaseFileWizard which seems to be +// dedicated to build "New Project" wizards. So, the plugin uses the base class in +// unconventional, matching its features to Boost.Build wizard needs, like: +// - no parent QWidget is used +// - platform name is set from default Kit display name, usually it's "Desktop" +// - extra values QVariantMap may carry custom data +// CAUTION: This wizard may stop building or start failing in run-time, +// if Qt Creator changes the base class significantly. +class OpenProjectWizard : public Core::BaseFileWizardFactory +{ + Q_OBJECT + +public: + OpenProjectWizard(Project const* const project); + + bool run(QString const& platform, QVariantMap const& extraValues); + + QVariantMap outputValues() const { return outputValues_; } + +protected: + + Core::BaseFileWizard* + create(QWidget* parent, Core::WizardDialogParameters const& parameters) const; + + Core::GeneratedFiles + generateFiles(QWizard const* baseWizard, QString* errorMessage) const; + + bool + postGenerateFiles(QWizard const* wizard + , Core::GeneratedFiles const& files, QString* errorMessage); + +private: + Project const* const project_; + QVariantMap outputValues_; + bool projectOpened_; +}; + +////////////////////////////////////////////////////////////////////////////////////////// +class OpenProjectWizardDialog : public Core::BaseFileWizard +{ + Q_OBJECT + +public: + OpenProjectWizardDialog(QWidget* parent, QString const& projectFile + , QVariantMap const& extraValues, QVariantMap& outputValues); + + QString path() const; + QString projectFile() const; + QString projectName() const; + QString defaultProjectName() const; + + QStringList selectedFiles() const; + QStringList selectedPaths() const; + +public slots: + void setProjectName(QString const& name); + +private: + QVariantMap& outputValues_; + QVariantMap extraValues_; + QString projectFile_; + PathsSelectionWizardPage* pathsPage_; + FilesSelectionWizardPage* filesPage_; +}; + +////////////////////////////////////////////////////////////////////////////////////////// +class PathsSelectionWizardPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit PathsSelectionWizardPage(OpenProjectWizardDialog* wizard); + + QString projectName() const; + void setProjectName(QString const& name); + +private: + OpenProjectWizardDialog* wizard_; + QLineEdit* nameLineEdit_; +}; + +} // namespace Internal +} // namespace BoostBuildProjectManager + +#endif // BBOPENPROJECTWIZARD_HPP diff --git a/src/plugins/boostbuildprojectmanager/b2outputparser.cpp b/src/plugins/boostbuildprojectmanager/b2outputparser.cpp new file mode 100644 index 0000000000..af552e3e03 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2outputparser.cpp @@ -0,0 +1,197 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#include "b2outputparser.h" +#include "b2utility.h" +#include "external/projectexplorer/gccparser.h" +#include "external/projectexplorer/ldparser.h" +#include "external/projectexplorer/clangparser.h" +// Qt Creator +#include +#include +#include +#include +// Qt +#include +#include +#include + +namespace BoostBuildProjectManager { +namespace Internal { + +namespace { +char const* const RxToolsetFromCommand = "^([\\w-]+)(?:\\.)([\\w-]+).+$"; +char const* const RxToolsetFromWarning = "^warning\\:.+toolset.+\\\"([\\w-]+)\\\".*$"; +// TODO: replace filename with full path? ^\*\*passed\*\*\s(.+\.test) +char const* const RxTestPassed = "^\\*\\*passed\\*\\*\\s+(.+\\.test)\\s*$"; +char const* const RxTestFailed = "^\\.\\.\\.failed\\s+testing\\.capture-output\\s+(.+\\.run)\\.\\.\\.$"; +char const* const RxTestFailedAsExpected = "^\\(failed-as-expected\\)\\s+(.+\\.o)\\s*$"; +char const* const RxTestFileLineN = "(\\.[ch][px]*)\\(([0-9]+)\\)"; // replace \\1:\\2 +char const* const RxTestFileObj = "\\W(\\w+)(\\.o)"; +} + +BoostBuildParser::BoostBuildParser() + : rxToolsetNameCommand_(QLatin1String(RxToolsetFromCommand)) + , rxToolsetNameWarning_(QLatin1String(RxToolsetFromWarning)) + , rxTestPassed_(QLatin1String(RxTestPassed)) + , rxTestFailed_(QLatin1String(RxTestFailed)) + , rxTestFailedAsExpected_(QLatin1String(RxTestFailedAsExpected)) + , rxTestFileLineN_(QLatin1String(RxTestFileLineN)) + , rxTestFileObj_(QLatin1String(RxTestFileObj)) + , lineMode_(Common) +{ + rxToolsetNameCommand_.setMinimal(true); + QTC_CHECK(rxToolsetNameCommand_.isValid()); + + rxToolsetNameWarning_.setMinimal(true); + QTC_CHECK(rxToolsetNameWarning_.isValid()); + + rxTestPassed_.setMinimal(true); + QTC_CHECK(rxTestPassed_.isValid()); + + rxTestFailed_.setMinimal(true); + QTC_CHECK(rxTestFailed_.isValid()); + + rxTestFailedAsExpected_.setMinimal(true); + QTC_CHECK(rxTestFailedAsExpected_.isValid()); + + rxTestFileLineN_.setMinimal(true); + QTC_CHECK(rxTestFileLineN_.isValid()); +} + +QString BoostBuildParser::findToolset(QString const& line) const +{ + QString name; + if (rxToolsetNameWarning_.indexIn(line) > -1) + name = rxToolsetNameWarning_.cap(1); + else if (rxToolsetNameCommand_.indexIn(line) > -1) + name = rxToolsetNameCommand_.cap(1); + return name; +} + +void BoostBuildParser::setToolsetParser(QString const& toolsetName) +{ + if (!toolsetName.isEmpty() && toolsetName_.isEmpty()) { + if (QStringRef(&toolsetName, 0, 3) == QLatin1String("gcc")) { + appendOutputParser(new ProjectExplorer::GccParser()); + toolsetName_ = toolsetName; + } else if (QStringRef(&toolsetName, 0, 5) == QLatin1String("clang")) { + // clang-xxx found (e.g. clang-linux) + appendOutputParser(new ProjectExplorer::ClangParser()); + toolsetName_ = toolsetName; + } else { + // TODO: Add more toolsets (Intel, VC++, etc.) + } + } +} + +void BoostBuildParser::stdOutput(QString const& rawLine) +{ + setToolsetParser(findToolset(rawLine)); + + QString const line + = rightTrimmed(rawLine).replace(rxTestFileLineN_, QLatin1String("\\1:\\2")); + if (!toolsetName_.isEmpty() && line.startsWith(toolsetName_)) + lineMode_ = Toolset; + else if (line.startsWith(QLatin1String("testing")) + || line.startsWith(QLatin1String("(failed-as-expected)"))) + lineMode_ = Testing; + else if (line.startsWith(QLatin1String("common"))) + lineMode_ = Common; + + // TODO: Why forwarding stdOutput to ProjectExplorer::IOutputParser::stdError? + // Because of a bug (or feature?) in Boost.Build: + // stdout and stderr not forwarded to respective channels + // https://svn.boost.org/trac/boost/ticket/9485 + + if (lineMode_ == Toolset) { + ProjectExplorer::IOutputParser::stdError(line); + } else if (lineMode_ == Testing) { + if (rxTestPassed_.indexIn(line) > -1) { + BBPM_QDEBUG(rxTestPassed_.capturedTexts()); + // TODO: issue #3 + ProjectExplorer::Task task(ProjectExplorer::Task::Unknown + , rxTestPassed_.cap(0) + , Utils::FileName::fromString(rxTestPassed_.cap(1)) + , -1 // line + , ProjectExplorer::Constants::TASK_CATEGORY_COMPILE); + setTask(task); + lineMode_ = Common; + } else if (rxTestFailed_.indexIn(line) > -1) { + BBPM_QDEBUG(rxTestFailed_.capturedTexts()); + + // Report summary task for "...failed testing.capture-output /myfile.run" + ProjectExplorer::Task task(ProjectExplorer::Task::Error + , rxTestFailed_.cap(0) + , Utils::FileName::fromString(rxTestFailed_.cap(1)) + , -1 // line + , ProjectExplorer::Constants::TASK_CATEGORY_COMPILE); + setTask(task); + + lineMode_ = Common; + } else if (rxTestFailedAsExpected_.indexIn(line) > -1) { + BBPM_QDEBUG(rxTestFailedAsExpected_.capturedTexts()); + + // TODO: Handling of "(failed-as-expected)" is not great, might be confusing. + // Boost.Build spits out compile command first, so compilation errors arrive + // and are parsed immediately (issue tasks are created) + // due to lineMode_==Toolset. + // Then, "(failed-as-expected)" status arrives and there seem to be no way to + // look back and clear all the issue tasks created for compilation errors. + // TODO: Ask Volodya if b2 could announce "testing." before compile command + // for a compile-time test. + + QString fileName(rxTestFailedAsExpected_.cap(1)); + if (rxTestFileObj_.indexIn(fileName)) + fileName = rxTestFileObj_.cap(1) + QLatin1String(".cpp");// FIXME:hardcoded ext + + // ATM, we can only indicate in UI that test failed-as-expected + ProjectExplorer::Task task(ProjectExplorer::Task::Error + , rxTestFailedAsExpected_.cap(0) + , Utils::FileName::fromString(fileName) + , -1 // line + , ProjectExplorer::Constants::TASK_CATEGORY_COMPILE); + setTask(task); + + lineMode_ = Common; + } else { + // Parses compilation errors of run-time tests, creates issue tasks + ProjectExplorer::IOutputParser::stdError(line); + } + } else { + doFlush(); + ProjectExplorer::IOutputParser::stdOutput(line); + } +} + +void BoostBuildParser::stdError(QString const& line) +{ + setToolsetParser(findToolset(line)); + ProjectExplorer::IOutputParser::stdError(line); +} + +void BoostBuildParser::doFlush() +{ + if (!lastTask_.isNull()) { + ProjectExplorer::Task t = lastTask_; + lastTask_.clear(); + emit addTask(t); + } +} + +void BoostBuildParser::setTask(ProjectExplorer::Task const& task) +{ + doFlush(); + lastTask_ = task; +} + +} // namespace Internal +} // namespace BoostBuildProjectManager diff --git a/src/plugins/boostbuildprojectmanager/b2outputparser.h b/src/plugins/boostbuildprojectmanager/b2outputparser.h new file mode 100644 index 0000000000..a2f54cf59c --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2outputparser.h @@ -0,0 +1,64 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBOUTPUTPARSER_HPP +#define BBOUTPUTPARSER_HPP + +// Qt Creator +#include +#include +// Qt +#include +#include + +namespace BoostBuildProjectManager { +namespace Internal { + +class BoostBuildParser : public ProjectExplorer::IOutputParser +{ + Q_OBJECT + +public: + BoostBuildParser(); + + void stdOutput(QString const& line); + void stdError(QString const& line); + +protected: + void doFlush(); + +private: + + QString findToolset(QString const& line) const; + void setToolsetParser(QString const& toolsetName); + void setTask(ProjectExplorer::Task const& task); + + QRegExp rxToolsetNameCommand_; // ".compile." command line + QRegExp rxToolsetNameWarning_; // "warning: " status line + QRegExp rxTestPassed_; // "**passed**" status line + QRegExp rxTestFailed_; // "...failed testing" status line + QRegExp rxTestFailedAsExpected_; // "(failed-as-expected)" status line + QRegExp rxTestFileLineN_; // file.cpp(XX) => file.cpp:XX + QRegExp rxTestFileObj_; // file.o => file.cpp + QString toolsetName_; + + // Boost.Build command mode relates to first command token in line. + enum LineMode { Common, Toolset, Testing }; + LineMode lineMode_; + + ProjectExplorer::Task lastTask_; + QPointer parser_; +}; + +} // namespace Internal +} // namespace BoostBuildProjectManager + +#endif // BBOUTPUTPARSER_HPP diff --git a/src/plugins/boostbuildprojectmanager/b2project.cpp b/src/plugins/boostbuildprojectmanager/b2project.cpp new file mode 100644 index 0000000000..2074c60ff6 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2project.cpp @@ -0,0 +1,306 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#include "b2buildconfiguration.h" +#include "b2buildstep.h" +#include "b2openprojectwizard.h" +#include "b2project.h" +#include "b2projectfile.h" +#include "b2projectmanager.h" +#include "b2projectmanagerconstants.h" +#include "b2projectnode.h" +#include "b2utility.h" +// Qt Creator +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// Qt +#include +#include +#include + +namespace BoostBuildProjectManager { +namespace Internal { + +Project::Project(ProjectManager* manager, QString const& fileName) + : manager_(manager) + , filePath_(fileName) + , projectFile_(new ProjectFile(this, filePath_)) // enables projectDirectory() + , projectNode_(new ProjectNode(this, projectFile_)) +{ + Q_ASSERT(manager_); + Q_ASSERT(!filePath_.isEmpty()); + + setProjectContext(Core::Context(Constants::PROJECT_CONTEXT)); + setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX)); +#if defined(IDE_VERSION_MAJOR) && (IDE_VERSION_MAJOR == 3 && IDE_VERSION_MINOR > 0) + setId(Constants::PROJECT_ID); +#endif + + QFileInfo const projectFileInfo(filePath_); + QDir const projectDir(projectFileInfo.dir()); + projectName_ = defaultProjectName(filePath_); + filesFilePath_ = QFileInfo(projectDir + , filePath_ + QLatin1String(Constants::EXT_JAMFILE_FILES)).absoluteFilePath(); + includesFilePath_ = QFileInfo(projectDir + , filePath_ + QLatin1String(Constants::EXT_JAMFILE_INCLUDES)).absoluteFilePath(); + + projectNode_->setDisplayName(displayName()); + + manager_->registerProject(this); + + // TODO: Add file watchers + //projectFileWatcher_->addPath(projectFilePath); + //connect(projectFileWatcher_, SIGNAL(fileChanged(QString)), this, SLOT(refresh())); + + BBPM_QDEBUG("created project: " << displayName() << " in " << projectDirectory()); +} + +Project::~Project() +{ + manager_->unregisterProject(this); + delete projectNode_; +} + +QString Project::displayName() const +{ + return projectName_; +} + +#if defined(IDE_VERSION_MAJOR) && (IDE_VERSION_MAJOR == 3 && IDE_VERSION_MINOR == 0) +Core::Id Project::id() const +{ + return Core::Id(Constants::PROJECT_ID); +} +#endif + +Core::IDocument* Project::document() const +{ + return projectFile_; +} + +ProjectExplorer::IProjectManager* Project::projectManager() const +{ + return manager_; +} + +ProjectExplorer::ProjectNode* Project::rootProjectNode() const +{ + return projectNode_; +} + +QStringList Project::files(FilesMode fileMode) const +{ + // TODO: handle ExcludeGeneratedFiles, but what files exactly? + // *.qtcreator.files, *.qtcreator.includes and *.user? + Q_UNUSED(fileMode); + return files_; +} + +QStringList Project::files() const +{ + return files(FilesMode::AllFiles); +} + +QString Project::filesFilePath() const +{ + Q_ASSERT(!filesFilePath_.isEmpty()); + return filesFilePath_; +} + +QString Project::includesFilePath() const +{ + Q_ASSERT(!includesFilePath_.isEmpty()); + return includesFilePath_; +} + +bool Project::needsConfiguration() const +{ + // TODO: Does Boost.Build project require any configuration on loading? + // - Kit selection + // - build/stage directory + // - targets listing + // CMakeProjectManager seems to request configuration in fromMap() + + return false; +} + +/*static*/ +QString Project::defaultProjectName(QString const& fileName) +{ + QFileInfo const fileInfo(fileName); + return fileInfo.absoluteDir().dirName(); +} + +/*static*/ +QString Project::defaultBuildDirectory(QString const& top) +{ + Utils::FileName fn(Utils::FileName::fromString(defaultWorkingDirectory(top))); + fn.appendPath(BBPM_C(BUILD_DIR_NAME)); + return fn.toString(); +} + +/*static*/ +QString Project::defaultWorkingDirectory(QString const& top) +{ + // Accepts both, project file or project directory, as top. + return ProjectExplorer::Project::projectDirectory(Utils::FileName::fromString(top)).toString(); +} + +void Project::setProjectName(QString const& name) +{ + if (projectName_ != name) { + projectName_ = name; + projectNode_->setDisplayName(projectName_); + // TODO: signal? + } +} + +QVariantMap Project::toMap() const +{ + QVariantMap map(ProjectExplorer::Project::toMap()); + map.insert(QLatin1String(Constants::P_KEY_PROJECTNAME), projectName_); + return map; +} + +// This function is called at the very beginning to restore the settings +// from .user file, if there is any with previous settings stored. +bool Project::fromMap(QVariantMap const& map) +{ + BBPM_QDEBUG(displayName()); + QTC_ASSERT(projectNode_, return false); + + if (!ProjectExplorer::Project::fromMap(map)) + return false; + + QVariantMap extraValues(map); + if (!extraValues.contains(BBPM_C(P_KEY_PROJECTNAME))) + extraValues.insert(BBPM_C(P_KEY_PROJECTNAME), projectName_); + setProjectName(map.value(BBPM_C(P_KEY_PROJECTNAME)).toString()); + + // Check required auxiliary files, run wizard of necessary + if (!QFileInfo(filesFilePath()).exists() || !QFileInfo(includesFilePath()).exists()) { + ProjectExplorer::Kit* defaultKit = ProjectExplorer::KitManager::defaultKit(); + Q_ASSERT(defaultKit); + + OpenProjectWizard wizard(this); + if (!wizard.run(defaultKit->displayName(), extraValues)) + return false; + + QVariantMap outputValues = wizard.outputValues(); + setProjectName(outputValues.value(BBPM_C(P_KEY_PROJECTNAME)).toString()); + } + + // Set up active ProjectConfiguration (aka Target). + // NOTE: Call setActiveBuildConfiguration when creating new build configurations. + if (!activeTarget()) { + // Create project configuration from scratch + + // TODO: Map the Kit to Boost.Build toolset option value + ProjectExplorer::Kit* defaultKit = ProjectExplorer::KitManager::defaultKit(); + Q_ASSERT(defaultKit); + + // Creates as many {Build|Run|Deploy}Configurations for as corresponding + // factories report as available. + // For example, BuildConfigurationFactory::availableBuilds => Debug and Release + ProjectExplorer::Target* target = createTarget(defaultKit); + QTC_ASSERT(target, return false); + + addTarget(target); + } else { + // Configure project from settings sorced from .user file + // TODO: Do we need to handle anything from .user here? Do we need this case? + BBPM_QDEBUG(displayName() << "has user file"); + } + + // Sanity check (taken from GenericProjectManager): + // We need both a BuildConfiguration and a RunConfiguration! + QList targetList = targets(); + foreach (ProjectExplorer::Target* t, targetList) { + if (!t->activeBuildConfiguration()) { + removeTarget(t); + continue; + } + if (!t->activeRunConfiguration()) + t->addRunConfiguration(new QtSupport::CustomExecutableRunConfiguration(t)); + } + + QTC_ASSERT(hasActiveBuildSettings(), return false); + QTC_ASSERT(activeTarget() != 0, return false); + + // Trigger loading project tree and parsing sources + refresh(); + + return true; +} + +void Project::refresh() +{ + QTC_ASSERT(QFileInfo(filesFilePath()).exists(), return); + QTC_ASSERT(QFileInfo(includesFilePath()).exists(), return); + + QSet oldFileList; + oldFileList = files_.toSet(); + + // Parse project: + // The manager does not parse Jamfile files. + // Only generates and parses list of source files in Jamfile.${JAMFILE_FILES_EXT} + QString const projectPath(projectDirectory().toString()); + filesRaw_ = Utility::readLines(filesFilePath()); + files_ = Utility::makeAbsolutePaths(projectPath, filesRaw_); + + QStringList includePaths = + Utility::makeAbsolutePaths(projectPath, + Utility::readLines(includesFilePath())); + + emit fileListChanged(); + + projectNode_->refresh(oldFileList); + + // TODO: Does it make sense to move this to separate asynchronous task? + // TODO: extract updateCppCodeModel + CppTools::CppModelManager *modelmanager = + CppTools::CppModelManager::instance(); + if (modelmanager) { + CppTools::ProjectInfo pinfo(this); + + CppTools::ProjectPartBuilder builder(pinfo); + //builder.setDefines(); // TODO: waiting for Jamfile parser + builder.setIncludePaths(QStringList() << projectDirectory().toString() << includePaths); + + const QList languages = builder.createProjectPartsForFiles(files_); + foreach (Core::Id language, languages) + setProjectLanguage(language, true); + + cppModelFuture_.cancel(); + pinfo.finish(); + cppModelFuture_ = modelmanager->updateProjectInfo(pinfo); + } +} + +} // namespace Internal +} // namespace BoostBuildProjectManager diff --git a/src/plugins/boostbuildprojectmanager/b2project.h b/src/plugins/boostbuildprojectmanager/b2project.h new file mode 100644 index 0000000000..6b8a813443 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2project.h @@ -0,0 +1,106 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBPROJECT_HPP +#define BBPROJECT_HPP + +// Qt Creator +#include +#include +#include +#include +// Qt +#include +#include +#include + +namespace BoostBuildProjectManager { +namespace Internal { + +class ProjectFile; +class ProjectManager; +class ProjectNode; + +// Represents a project node in the project explorer. +class Project : public ProjectExplorer::Project +{ + Q_OBJECT + +public: + Project(ProjectManager* manager, QString const& fileName); + ~Project(); + +#if defined(IDE_VERSION_MAJOR) && (IDE_VERSION_MAJOR == 3 && IDE_VERSION_MINOR == 0) + Core::Id id() const; +#endif + QString displayName() const; + Core::IDocument* document() const; + ProjectExplorer::IProjectManager* projectManager() const; + ProjectExplorer::ProjectNode* rootProjectNode() const; + QStringList files(FilesMode fileMode) const; + + bool needsConfiguration() const; + + void refresh(); + + QString filesFilePath() const; + QStringList files() const; + + QString includesFilePath() const; + + static QString defaultProjectName(QString const& fileName); + static QString defaultBuildDirectory(QString const& top); + static QString defaultWorkingDirectory(QString const& top); + +protected: + + QVariantMap toMap() const; + + // Deserializes all project data from the map object + // Calls the base ProjectExplorer::Project::fromMap function first. + bool fromMap(QVariantMap const& map); + +private: + + void setProjectName(QString const& name); + + // Corresponding project manager passed to the constructor + ProjectManager* manager_; + + // By default, name of directory with Jamfile. + // Boost.Build treats each Jamfile is a separate project, + // where hierarchy of Jamfiles makes hierarchy of projects. + QString projectName_; + + // Jamfile passed to the constructor (Jamroot, Jamfile, Jamfile.v2). + QString filePath_; + + // Auxiliary file Jamfile.${JAMFILE_FILES_EXT} with list of source files. + // Role of this file is similar to the .files file in the GenericProjectManager, + // hence managing of this file is implemented in very similar manner. + QString filesFilePath_; + QStringList files_; + QStringList filesRaw_; + QHash entriesRaw_; + + // Auxiliary file Jamfile.${JAMFILE_INCLUDES_EXT} with list of source files. + QString includesFilePath_; + + ProjectFile* projectFile_; + ProjectNode* projectNode_; + + QFuture cppModelFuture_; +}; + +} // namespace Internal +} // namespace BoostBuildProjectManager + +#endif // BBPROJECT_HPP diff --git a/src/plugins/boostbuildprojectmanager/b2projectfile.cpp b/src/plugins/boostbuildprojectmanager/b2projectfile.cpp new file mode 100644 index 0000000000..bcfeef9eaf --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2projectfile.cpp @@ -0,0 +1,80 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#include "b2project.h" +#include "b2projectfile.h" +#include "b2projectmanagerconstants.h" +#include "b2utility.h" + +namespace BoostBuildProjectManager { +namespace Internal { + +ProjectFile::ProjectFile(Project* project, QString const& fileName) + : Core::IDocument(project) + , project_(project) +{ + Q_ASSERT(!fileName.isEmpty()); + + setFilePath(Utils::FileName::fromString(fileName)); + + BBPM_QDEBUG(fileName); +} + +bool ProjectFile::save(QString* errorString, QString const& fileName, bool autoSave) +{ + Q_UNUSED(errorString); + Q_UNUSED(fileName); + Q_UNUSED(autoSave); + + BBPM_QDEBUG("TODO"); + return false; +} + +QString ProjectFile::defaultPath() const +{ + BBPM_QDEBUG("TODO"); + return QString(); +} + +QString ProjectFile::suggestedFileName() const +{ + return QString(); +} + +QString ProjectFile::mimeType() const +{ + BBPM_QDEBUG("TODO"); + return QLatin1String(Constants::MIMETYPE_JAMFILE); +} + +bool ProjectFile::isModified() const +{ + return false; +} + +bool ProjectFile::isSaveAsAllowed() const +{ + BBPM_QDEBUG("TODO"); + return false; +} + +bool ProjectFile::reload(QString* errorString, ReloadFlag flag, ChangeType type) +{ + Q_UNUSED(errorString); + Q_UNUSED(flag); + Q_UNUSED(type); + + BBPM_QDEBUG("TODO"); + return false; +} + +} // namespace Internal +} // namespace AutotoolsProjectManager diff --git a/src/plugins/boostbuildprojectmanager/b2projectfile.h b/src/plugins/boostbuildprojectmanager/b2projectfile.h new file mode 100644 index 0000000000..5e440df646 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2projectfile.h @@ -0,0 +1,47 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBPROJECTFILE_HPP +#define BBPROJECTFILE_HPP + +#include + +namespace BoostBuildProjectManager { +namespace Internal { + +class Project; + +// Describes the root of a project for use in Project class. +// In the context of Boost.Build the implementation is mostly empty, +// as the modification of a project is done by editing Jamfile.v2 files. +class ProjectFile : public Core::IDocument +{ + Q_OBJECT + +public: + ProjectFile(Project* project, QString const& fileName); + + bool save(QString* errorString, QString const& fileName, bool autoSave); + QString defaultPath() const; + QString suggestedFileName() const; + QString mimeType() const; + bool isModified() const; + bool isSaveAsAllowed() const; + bool reload(QString* errorString, ReloadFlag flag, ChangeType type); + +private: + Project* project_; +}; + +} // namespace Internal +} // namespace AutotoolsProjectManager + +#endif // BBPROJECTFILE_HPP diff --git a/src/plugins/boostbuildprojectmanager/b2projectmanager.cpp b/src/plugins/boostbuildprojectmanager/b2projectmanager.cpp new file mode 100644 index 0000000000..dcc4f439d8 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2projectmanager.cpp @@ -0,0 +1,64 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#include "b2projectmanager.h" +#include "b2projectmanagerconstants.h" +#include "b2project.h" +#include "b2utility.h" +// Qt Creator +#include +// Qt +#include +#include + +namespace BoostBuildProjectManager { +namespace Internal { + +ProjectManager::ProjectManager() +{ +} + +QString ProjectManager::mimeType() const +{ + BBPM_QDEBUG(Constants::MIMETYPE_PROJECT); + + return QLatin1String(Constants::MIMETYPE_PROJECT); +} + +ProjectExplorer::Project* +ProjectManager::openProject(QString const& fileName, QString* errorString) +{ + BBPM_QDEBUG("opening project:" << fileName); + + if (!QFileInfo(fileName).isFile()) { + if (errorString) + *errorString = tr("Failed opening project \"%1\": Project is not a file.") + .arg(fileName); + return 0; + } + + return new Project(this, fileName); +} + +void ProjectManager::registerProject(Project* project) +{ + Q_ASSERT(project); + projects_.append(project); +} + +void ProjectManager::unregisterProject(Project* project) +{ + Q_ASSERT(project); + projects_.removeAll(project); +} + +} // namespace Internal +} // namespace BoostBuildProjectManager diff --git a/src/plugins/boostbuildprojectmanager/b2projectmanager.h b/src/plugins/boostbuildprojectmanager/b2projectmanager.h new file mode 100644 index 0000000000..d4222beb61 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2projectmanager.h @@ -0,0 +1,54 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBPROJECTMANAGER_HPP +#define BBPROJECTMANAGER_HPP + +// Qt Creator +#include +// Qt +#include +#include + +namespace ProjectExplorer { +class Project; +} + +namespace BoostBuildProjectManager { +namespace Internal { + +class Project; + +// Sole implementation of the IProjectManager class for the extension. +class ProjectManager : public ProjectExplorer::IProjectManager +{ + Q_OBJECT + +public: + ProjectManager(); + + QString mimeType() const; + + // Creates new instance of Project class. + ProjectExplorer::Project* + openProject(QString const& fileName, QString* errorString); + + void registerProject(Project* project); + void unregisterProject(Project* project); + +private: + QList projects_; +}; + +} // namespace Internal +} // namespace BoostBuildProjectManager + +#endif // BBPROJECTMANAGER_HPP diff --git a/src/plugins/boostbuildprojectmanager/b2projectmanager_global.h b/src/plugins/boostbuildprojectmanager/b2projectmanager_global.h new file mode 100644 index 0000000000..3287ab357d --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2projectmanager_global.h @@ -0,0 +1,23 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBPROJECTMANAGER_GLOBAL_HPP +#define BBPROJECTMANAGER_GLOBAL_HPP + +#include + +#if defined(BOOSTBUILDPROJECTMANAGER_LIBRARY) +# define BOOSTBUILDPROJECTMANAGER_EXPORT Q_DECL_EXPORT +#else +# define BOOSTBUILDPROJECTMANAGER_EXPORT Q_DECL_IMPORT +#endif + +#endif // BBPROJECTMANAGER_GLOBAL_HPP diff --git a/src/plugins/boostbuildprojectmanager/b2projectmanagerconstants.h b/src/plugins/boostbuildprojectmanager/b2projectmanagerconstants.h new file mode 100644 index 0000000000..a6db0a1b5b --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2projectmanagerconstants.h @@ -0,0 +1,68 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBPROJECTMANAGERCONSTANTS_HPP +#define BBPROJECTMANAGERCONSTANTS_HPP + +#include + +namespace BoostBuildProjectManager { +namespace Constants { + +char const BOOSTBUILD[] = "Boost.Build"; + +char const PROJECT_CONTEXT[] = "BoostBuildProjectManager.ProjectContext"; +char const PROJECT_ID[] = "BoostBuildProjectManager.Project"; +char const PROJECT_WIZARD_ID[] = "BoostBuildProjectManager.Project.Wizard"; +char const PROJECT_READER_TASK_ID[] = "BoostBuildProjectManager.ProjectReader.Task"; + +char const BUILDCONFIGURATION_ID[] = "BoostBuildProjectManager.BuildConfiguration"; +char const BUILDSTEP_ID[] = "BoostBuildProjectManager.BuildStep"; + +// Keys for class map registry +char const P_KEY_PROJECTNAME[] = "BoostBuildProjectManager.Project.ProjectName"; +char const BC_KEY_WORKDIR[] = "BoostBuildProjectManager.BuildConfiguration.WorkingDirectory"; +char const BS_KEY_CLEAN[] = "BoostBuildProjectManager.BuildStep.Clean"; +char const BS_KEY_ARGUMENTS[] = "BoostBuildProjectManager.BuildStep.AdditionalArguments"; + +// MIME types and file patterns +char const MIMETYPE_PROJECT[] = "text/x-boostbuild-project"; +char const MIMETYPE_JAMFILE[] = "application/vnd.boostbuild.v2"; +char const MIMETYPE_JAMFILE_FILES[] = "application/vnd.qtcreator.boostbuild.files"; +const char MIMETYPE_JAMFILE_INCLUDES[] = "application/vnd.qtcreator.boostbuild.includes"; +char const EXT_JAMFILE_FILES[] = ".qtcreator.files"; +char const EXT_JAMFILE_INCLUDES[] = ".qtcreator.includes"; + +// Command and options +char const COMMAND_BB2[] = "b2"; +char const COMMAND_BJAM[] = "bjam"; + +char const VARIANT_DEBUG[] = QT_TR_NOOP("Debug"); +char const VARIANT_RELEASE[] = QT_TR_NOOP("Release"); + +// ${BOOST}/tools/build/v2/doc/src/architecture.xml +// Since Boost.Build almost always generates targets under the "bin" +char const BUILD_DIR_NAME[] = "bin"; + +// FileSelectionWizardPage +char const HIDE_FILE_FILTER_SETTING[] = "BoostBuildProjectManager/FileFilter"; +char const HIDE_FILE_FILTER_DEFAULT[] = "Makefile*; *.o; *.obj; *~; *.files; *.config; *.creator; *.user; *.includes; *.autosave"; +char const SHOW_FILE_FILTER_SETTING[] = "BoostBuildProjectManager/ShowFileFilter"; +char const SHOW_FILE_FILTER_DEFAULT[] = "*.c; *.cc; *.cpp; *.cp; *.cxx; *.c++; *.h; *.hh; *.h; *.hxx;"; + +// Meaningful names for common boolean values +bool const FileNotGenerated = false; +bool const ReturnValueNotIgnored = false; + +} // namespace Constants +} // namespace BoostBuildProjectManager + +#endif // BBPROJECTMANAGERCONSTANTS_HPP diff --git a/src/plugins/boostbuildprojectmanager/b2projectmanagerplugin.cpp b/src/plugins/boostbuildprojectmanager/b2projectmanagerplugin.cpp new file mode 100644 index 0000000000..b5c3ae1186 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2projectmanagerplugin.cpp @@ -0,0 +1,83 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#include "b2buildconfiguration.h" +#include "b2buildstep.h" +#include "b2projectmanager.h" +#include "b2projectmanagerplugin.h" +#include "b2projectmanagerconstants.h" +#include +#include +#include +#include +#include +#include +#include +// Qt +#include +#include +#include +#include + +#include + +namespace BoostBuildProjectManager { namespace Internal { + +BoostBuildPlugin::BoostBuildPlugin() +{ + // Create your members +} + +BoostBuildPlugin::~BoostBuildPlugin() +{ + // Unregister objects from the plugin manager's object pool + // Delete members +} + +bool BoostBuildPlugin::initialize(QStringList const& arguments, QString* errorString) +{ + Q_UNUSED(arguments) + Q_UNUSED(errorString) + + // Register objects in the plugin manager's object pool + // Load settings + // Add actions to menus + // Connect to other plugins' signals + // In the initialize function, a plugin can be sure that the plugins it + // depends on have initialized their members. + + QLatin1String const mimeTypes(":boostbuildproject/BoostBuildProjectManager.mimetypes.xml"); + Utils::MimeDatabase::addMimeTypes(mimeTypes); + + addAutoReleasedObject(new BuildStepFactory); + addAutoReleasedObject(new BuildConfigurationFactory); + //TODO addAutoReleasedObject(new RunConfigurationFactory); + addAutoReleasedObject(new ProjectManager); + + return true; +} + +void BoostBuildPlugin::extensionsInitialized() +{ + // Retrieve objects from the plugin manager's object pool + // In the extensionsInitialized function, a plugin can be sure that all + // plugins that depend on it are completely initialized. +} + +ExtensionSystem::IPlugin::ShutdownFlag BoostBuildPlugin::aboutToShutdown() +{ + // Save settings + // Disconnect from signals that are not needed during shutdown + // Hide UI (if you add UI that is not in the main window directly) + return SynchronousShutdown; +} + +}} diff --git a/src/plugins/boostbuildprojectmanager/b2projectmanagerplugin.h b/src/plugins/boostbuildprojectmanager/b2projectmanagerplugin.h new file mode 100644 index 0000000000..e54dcaabeb --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2projectmanagerplugin.h @@ -0,0 +1,48 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBPROJECTMANAGERPLUGIN_HPP +#define BBPROJECTMANAGERPLUGIN_HPP + +#include "b2projectmanager_global.h" + +#include + +namespace BoostBuildProjectManager { +namespace Internal { + + +// Sole implementation of the IPlugin class for the extension. +class BoostBuildPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "BoostBuildProjectManager.json") + +public: + BoostBuildPlugin(); + ~BoostBuildPlugin(); + + // Called as second step of the plugins loading. + // The initialize functions are called in root-to-leaf order of the dependency tree. + bool initialize(QStringList const& arguments, QString* errorString); + + // Called as final step of the plugins loading. + // At this point, all dependencies along the plugin dependency tree + // have been initialized completely. + void extensionsInitialized(); + + ShutdownFlag aboutToShutdown(); +}; + +} // namespace Internal +} // namespace BoostBuildProjectManager + +#endif // BBPROJECTMANAGERPLUGIN_HPP diff --git a/src/plugins/boostbuildprojectmanager/b2projectnode.cpp b/src/plugins/boostbuildprojectmanager/b2projectnode.cpp new file mode 100644 index 0000000000..2c97b565cf --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2projectnode.cpp @@ -0,0 +1,260 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#include "b2projectnode.h" +#include "b2project.h" +#include "b2utility.h" +// Qt Creator +#include +#include +#include +// Qt +#include +#include +#include +#include +#include +#include + +namespace BoostBuildProjectManager { +namespace Internal { + +ProjectNode::ProjectNode(Project* project, Core::IDocument* projectFile) + : ProjectExplorer::ProjectNode(projectFile->filePath()) + , project_(project) + , projectFile_(projectFile) +{ + // TODO: setDisplayName(QFileInfo(projectFile->filePath()).completeBaseName()); +} + +bool ProjectNode::hasBuildTargets() const +{ + return false; +} + +QList +ProjectNode::supportedActions(Node* node) const +{ + Q_UNUSED(node); + + // TODO: Jamfiles (auto)editing not supported, does it make sense to manage files? + return QList(); +} + +bool ProjectNode::canAddSubProject(QString const& filePath) const +{ + Q_UNUSED(filePath) + return false; +} + +bool ProjectNode::addSubProjects(QStringList const& filePaths) +{ + Q_UNUSED(filePaths) + return false; +} + +bool ProjectNode::removeSubProjects(QStringList const& filePath) +{ + Q_UNUSED(filePath) + return false; +} + +bool ProjectNode::addFiles(QStringList const& filePaths, QStringList* notAdded = 0) +{ + Q_UNUSED(filePaths); + Q_UNUSED(notAdded); + return false; +} + +bool ProjectNode::removeFiles(QStringList const& filePaths, QStringList* notRemoved = 0) +{ + Q_UNUSED(filePaths); + Q_UNUSED(notRemoved); + return false; +} + +bool ProjectNode::deleteFiles(QStringList const& filePaths) +{ + Q_UNUSED(filePaths); + return false; +} + +bool ProjectNode::renameFile(QString const& filePath, QString const& newFilePath) +{ + Q_UNUSED(filePath); + Q_UNUSED(newFilePath); + return false; +} + +QList ProjectNode::runConfigurationsFor(Node* node) +{ + Q_UNUSED(node); + return QList(); +} + +void ProjectNode::refresh(QSet oldFileList) +{ + // The idea of refreshing files in project explorer taken from GenericProjectManager + + // Only do this once, at first run. + if (oldFileList.isEmpty()) { + using ProjectExplorer::FileNode; + FileNode* projectFileNode = new FileNode(project_->projectFilePath() + , ProjectExplorer::ProjectFileType + , Constants::FileNotGenerated); + + FileNode* filesFileNode = new FileNode(Utils::FileName::fromString(project_->filesFilePath()) + , ProjectExplorer::ProjectFileType + , Constants::FileNotGenerated); + + FileNode* includesFileNode = new FileNode(Utils::FileName::fromString(project_->includesFilePath()) + , ProjectExplorer::ProjectFileType + , Constants::FileNotGenerated); + + addFileNodes(QList() + << projectFileNode << filesFileNode << includesFileNode); + } + + oldFileList.remove(project_->filesFilePath()); + oldFileList.remove(project_->includesFilePath()); + + QSet newFileList = project_->files().toSet(); + newFileList.remove(project_->filesFilePath()); + newFileList.remove(project_->includesFilePath()); + + // Calculate set of added and removed files + QSet removed = oldFileList; + removed.subtract(newFileList); + QSet added = newFileList; + added.subtract(oldFileList); + + typedef QHash FilesInPaths; + typedef FilesInPaths::ConstIterator FilesInPathsIterator; + using ProjectExplorer::FileNode; + using ProjectExplorer::FileType; + QString const baseDir = QFileInfo(path().toString()).absolutePath(); + + // Process all added files + FilesInPaths filesInPaths = Utility::sortFilesIntoPaths(baseDir, added); + for (FilesInPathsIterator it = filesInPaths.constBegin(), + cend = filesInPaths.constEnd(); it != cend; ++it) { + // Create node + QString const& filePath = it.key(); + QStringList parts; + if (!filePath.isEmpty()) + parts = filePath.split(QLatin1Char('/')); + + FolderNode* folder = findFolderByName(parts, parts.size()); + if (!folder) + folder = createFolderByName(parts, parts.size()); + + // Attach content to node + QList fileNodes; + foreach (QString const& file, it.value()) { + // TODO: handle various types, mime to FileType + FileType fileType = ProjectExplorer::SourceType; + FileNode* fileNode = new FileNode(Utils::FileName::fromString(file), fileType, Constants::FileNotGenerated); + fileNodes.append(fileNode); + } + + addFileNodes(fileNodes); + } + + // Process all removed files + filesInPaths = Utility::sortFilesIntoPaths(baseDir, removed); + for (FilesInPathsIterator it = filesInPaths.constBegin(), + cend = filesInPaths.constEnd(); it != cend; ++it) { + // Create node + QString const& filePath = it.key(); + QStringList parts; + if (!filePath.isEmpty()) + parts = filePath.split(QLatin1Char('/')); + FolderNode* folder = findFolderByName(parts, parts.size()); + + QList fileNodes; + foreach (QString const& file, it.value()) { + foreach (FileNode* fn, folder->fileNodes()) + if (fn->path() == Utils::FileName::fromString(file)) + fileNodes.append(fn); + } + + removeFileNodes(fileNodes); + } + + // Clean up + foreach (FolderNode* fn, subFolderNodes()) + removeEmptySubFolders(this, fn); + +} + +void ProjectNode::removeEmptySubFolders(FolderNode* parent, FolderNode* subParent) +{ + foreach (FolderNode* fn, subParent->subFolderNodes()) + removeEmptySubFolders(subParent, fn); + + if (subParent->subFolderNodes().isEmpty() && subParent->fileNodes().isEmpty()) + removeFolderNodes(QList() << subParent); +} + +QString appendPathComponents(QStringList const& components, int const end) +{ + QString folderName; + for (int i = 0; i < end; ++i) { + folderName.append(components.at(i)); + folderName += QLatin1Char('/'); + } + return folderName; +} + +ProjectExplorer::FolderNode* +ProjectNode::createFolderByName(QStringList const& components, int const end) +{ + if (end == 0) + return this; + + using ProjectExplorer::FolderNode; + QString const baseDir = QFileInfo(path().toString()).path(); + QString const folderName = appendPathComponents(components, end); + FolderNode* folder = new FolderNode(Utils::FileName::fromString(baseDir + QLatin1Char('/') + folderName)); + folder->setDisplayName(components.at(end - 1)); + + FolderNode* parent = findFolderByName(components, end - 1); + if (!parent) + parent = createFolderByName(components, end - 1); + addFolderNodes(QList() << folder); + + return folder; +} + +ProjectExplorer::FolderNode* +ProjectNode::findFolderByName(QStringList const& components, int const end) const +{ + if (end == 0) + return const_cast(this); + + using ProjectExplorer::FolderNode; + FolderNode *parent = findFolderByName(components, end - 1); + if (!parent) + return 0; + + QString const folderName = appendPathComponents(components, end); + QString const baseDir = QFileInfo(path().toString()).path(); + foreach (FolderNode* fn, parent->subFolderNodes()) { + if (fn->path() == Utils::FileName::fromString(baseDir + QLatin1Char('/') + folderName)) + return fn; + } + + return 0; +} + +} // namespace Internal +} // namespace BoostBuildProjectManager diff --git a/src/plugins/boostbuildprojectmanager/b2projectnode.h b/src/plugins/boostbuildprojectmanager/b2projectnode.h new file mode 100644 index 0000000000..9a1e5422da --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2projectnode.h @@ -0,0 +1,77 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBPROJECTNODE_HPP +#define BBPROJECTNODE_HPP + +// Qt Creator +#include +// Qt +#include +#include +#include +#include +#include + +namespace Core { +class IDocument; +} + +namespace ProjectExplorer { +class RunConfiguration; +} + +namespace BoostBuildProjectManager { +namespace Internal { + +class Project; + +// An in-memory presentation of a Project. +// Represents a file or a folder of the project tree. +// No special operations (addFiles(), removeFiles(), renameFile(), etc.) are offered. +class ProjectNode : public ProjectExplorer::ProjectNode +{ + +public: + ProjectNode(Project* project, Core::IDocument* projectFile); + + bool hasBuildTargets() const; + QList supportedActions(Node* node) const; + bool canAddSubProject(QString const& filePath) const; + bool addSubProjects(QStringList const& filePaths); + bool removeSubProjects(QStringList const& filePaths); + bool addFiles(QStringList const& filePaths, QStringList* notAdded /*= 0*/); + bool removeFiles(QStringList const& filePaths, QStringList* notRemoved /*= 0*/); + bool deleteFiles(QStringList const& filePaths); + bool renameFile(QString const& filePath, QString const& newFilePath); + QList runConfigurationsFor(Node* node); + + void refresh(QSet oldFileList); + +private: + + ProjectExplorer::FolderNode* + createFolderByName(QStringList const& components, int end); + + ProjectExplorer::FolderNode* + findFolderByName(QStringList const& components, int end) const; + + void removeEmptySubFolders(FolderNode* parent, FolderNode* subParent); + + Project* project_; + Core::IDocument* projectFile_; +}; + +} // namespace Internal +} // namespace BoostBuildProjectManager + +#endif // BBPROJECTNODE_HPP diff --git a/src/plugins/boostbuildprojectmanager/b2utility.cpp b/src/plugins/boostbuildprojectmanager/b2utility.cpp new file mode 100644 index 0000000000..376d26071d --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2utility.cpp @@ -0,0 +1,151 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#include "b2utility.h" +// Qt Creator +#include +#include +// Qt +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace BoostBuildProjectManager { +namespace Utility { + +QStringList readLines(QString const& filePath) +{ + QFileInfo const fileInfo(filePath); + QStringList lines; + + QFile file(fileInfo.absoluteFilePath()); + if (file.open(QFile::ReadOnly)) { + QTextStream stream(&file); + + forever { + QString line = stream.readLine(); + if (line.isNull()) + break; + + lines.append(line); + } + } + + return lines; +} + +QStringList makeAbsolutePaths(QString const& basePath, QStringList const& paths) +{ + QDir const baseDir(basePath); + + QFileInfo fileInfo; + QStringList absolutePaths; + foreach (QString const& path, paths) { + QString trimmedPath = path.trimmed(); + if (!trimmedPath.isEmpty()) { + trimmedPath = Utils::FileName::fromUserInput(trimmedPath).toString(); + + fileInfo.setFile(baseDir, trimmedPath); + if (fileInfo.exists()) { + QString const absPath = fileInfo.absoluteFilePath(); + Q_ASSERT(!absPath.isEmpty()); + absolutePaths.append(absPath); + } + } + } + absolutePaths.removeDuplicates(); + return absolutePaths; +} + +QStringList& makeRelativePaths(QString const& basePath, QStringList& paths) +{ + QDir const baseDir(basePath); + for (QStringList::iterator it = paths.begin(), end = paths.end(); it != end; ++it) + *it = baseDir.relativeFilePath(*it); + return paths; +} + +QHash sortFilesIntoPaths(QString const& basePath + , QSet const& files) +{ + QHash filesInPath; + QDir const baseDir(basePath); + + foreach (QString const& absoluteFileName, files) { + QFileInfo const fileInfo(absoluteFileName); + Utils::FileName absoluteFilePath = Utils::FileName::fromString(fileInfo.path()); + QString relativeFilePath; + + if (absoluteFilePath.isChildOf(baseDir)) { + relativeFilePath = absoluteFilePath.relativeChildPath( + Utils::FileName::fromString(basePath)).toString(); + } else { + // `file' is not part of the project. + relativeFilePath = baseDir.relativeFilePath(absoluteFilePath.toString()); + if (relativeFilePath.endsWith(QLatin1Char('/'))) + relativeFilePath.chop(1); + } + + filesInPath[relativeFilePath].append(absoluteFileName); + } + return filesInPath; +} + +// Parses Jamfile and looks for project rule to extract project name. +// Boost.Build project rule has the following syntax: +// project id : attributes ; +// The project definition can span across multiple lines, including empty lines. +// but each syntax token must be separated with a whitespace.. +QString parseJamfileProjectName(QString const& fileName) +{ + QString projectName; + QFile file(fileName); + if (file.exists()) { + // Jamfile project rule tokens to search for + QString const ruleBeg(QLatin1String("project")); + QChar const ruleEnd(QLatin1Char(';')); + QChar const attrSep(QLatin1Char(':')); + QChar const tokenSep(QLatin1Char(' ')); // used to ensure tokens separation + QString projectDef; // buffer for complete project definition + + file.open(QIODevice::ReadOnly | QIODevice::Text); + QTextStream stream(&file); + while (!stream.atEnd()) { + QString const line(stream.readLine()); + if (projectDef.isEmpty() && line.trimmed().startsWith(ruleBeg)) + projectDef.append(line + tokenSep); + else if (!projectDef.isEmpty()) + projectDef.append(line + tokenSep); + + if (projectDef.contains(attrSep) || projectDef.contains(ruleEnd)) + break; + } + + if (!projectDef.isEmpty()) { + QRegExp rx(QLatin1String("\\s*project\\s+([a-zA-Z\\-\\/]+)\\s+[\\:\\;]?")); + rx.setMinimal(true); + QTC_CHECK(rx.isValid()); + if (rx.indexIn(projectDef) > -1) + projectName = rx.cap(1); + } + } + return projectName; +} + +} // namespace Utility +} // namespace BoostBuildProjectManager diff --git a/src/plugins/boostbuildprojectmanager/b2utility.h b/src/plugins/boostbuildprojectmanager/b2utility.h new file mode 100644 index 0000000000..2f810cf35c --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/b2utility.h @@ -0,0 +1,60 @@ +// +// Copyright (C) 2013 Mateusz Łoskot +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// +// This file is part of Qt Creator Boost.Build plugin project. +// +// This is free software; you can redistribute and/or modify it under +// the terms of the GNU Lesser General Public License, Version 2.1 +// as published by the Free Software Foundation. +// See accompanying file LICENSE.txt or copy at +// http://www.gnu.org/licenses/lgpl-2.1-standalone.html. +// +#ifndef BBUTILITY_HPP +#define BBUTILITY_HPP + +#include "b2projectmanagerconstants.h" +// Qt +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG + +#define BBPM_QDEBUG(msg) \ + qDebug() \ + << "[" << BoostBuildProjectManager::Constants::BOOSTBUILD << "] " \ + << "(" << __PRETTY_FUNCTION__ << ")"; \ + qDebug().nospace() << "\t" << msg + +#else +#define BBPM_QDEBUG(msg) + +#endif // _DEBUG + +#define BBPM_C(CONSTANT) QLatin1String(BoostBuildProjectManager::Constants::CONSTANT) + +////////////////////////////////////////////////////////////////////////////////////////// +namespace BoostBuildProjectManager { +namespace Utility { + +// Read all lines from a file. +QStringList readLines(QString const& absoluteFileName); + +// Converts the path from relative to the project to an absolute path. +QStringList makeAbsolutePaths(QString const& basePath, QStringList const& paths); + +QStringList& makeRelativePaths(QString const& basePath, QStringList& paths); + +QHash sortFilesIntoPaths(QString const& basePath + , QSet const& files); + +QString parseJamfileProjectName(QString const& fileName); + +} // namespace Utility +} // namespace BoostBuildProjectManager + +#endif // BBUTILITY_HPP diff --git a/src/plugins/boostbuildprojectmanager/boostbuildproject.qrc b/src/plugins/boostbuildprojectmanager/boostbuildproject.qrc new file mode 100644 index 0000000000..44d7c94bbc --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/boostbuildproject.qrc @@ -0,0 +1,5 @@ + + + BoostBuildProjectManager.mimetypes.xml + + diff --git a/src/plugins/boostbuildprojectmanager/boostbuildprojectmanager.pro b/src/plugins/boostbuildprojectmanager/boostbuildprojectmanager.pro new file mode 100644 index 0000000000..c743db04d0 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/boostbuildprojectmanager.pro @@ -0,0 +1,46 @@ +include(../../qtcreatorplugin.pri) + +HEADERS += \ + b2buildconfiguration.h \ + b2buildinfo.h \ + b2buildstep.h \ + b2openprojectwizard.h \ + b2outputparser.h \ + b2project.h \ + b2projectfile.h \ + b2projectmanager.h \ + b2projectmanager_global.h \ + b2projectmanagerconstants.h \ + b2projectmanagerplugin.h \ + b2projectnode.h \ + b2utility.h \ + filesselectionwizardpage.h \ + selectablefilesmodel.h \ + external/projectexplorer/clangparser.h \ + external/projectexplorer/gccparser.h \ + external/projectexplorer/ldparser.h + +SOURCES += \ + b2buildconfiguration.cpp \ + b2buildinfo.cpp \ + b2buildstep.cpp \ + b2openprojectwizard.cpp \ + b2outputparser.cpp \ + b2project.cpp \ + b2projectfile.cpp \ + b2projectmanager.cpp \ + b2projectmanagerplugin.cpp \ + b2projectnode.cpp \ + b2utility.cpp \ + filesselectionwizardpage.cpp \ + selectablefilesmodel.cpp \ + external/projectexplorer/clangparser.cpp \ + external/projectexplorer/gccparser.cpp \ + external/projectexplorer/ldparser.cpp + +RESOURCES += \ + boostbuildproject.qrc + +OTHER_FILES += \ + $${QTC_PLUGIN_NAME}.mimetypes.xml \ + $${QTC_PLUGIN_NAME}.pluginspec.in diff --git a/src/plugins/boostbuildprojectmanager/boostbuildprojectmanager_dependencies.pri b/src/plugins/boostbuildprojectmanager/boostbuildprojectmanager_dependencies.pri new file mode 100644 index 0000000000..3e8bb8694a --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/boostbuildprojectmanager_dependencies.pri @@ -0,0 +1,12 @@ +QTC_PLUGIN_NAME = BoostBuildProjectManager +QTC_LIB_DEPENDS += \ + extensionsystem \ + utils +QTC_PLUGIN_DEPENDS += \ + coreplugin \ + projectexplorer \ + cpptools \ + texteditor \ + qtsupport +QTC_TEST_DEPENDS += \ + cppeditor diff --git a/src/plugins/boostbuildprojectmanager/external/projectexplorer/clangparser.cpp b/src/plugins/boostbuildprojectmanager/external/projectexplorer/clangparser.cpp new file mode 100644 index 0000000000..df648bc5fd --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/external/projectexplorer/clangparser.cpp @@ -0,0 +1,270 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License Version 2.1 by the Free Software +** Foundation and appearing in the file LICENSE.txt included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License Version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "clangparser.h" +#include "ldparser.h" +#include + +using namespace ProjectExplorer; + +// opt. drive letter + filename: (2 brackets) +static const char * const FILE_PATTERN = "(|([A-Za-z]:)?[^:]+\\.[^:]+)"; + +ClangParser::ClangParser() : + m_commandRegExp(QLatin1String("^clang(\\+\\+)?: +(fatal +)?(warning|error|note): (.*)$")), + m_inLineRegExp(QLatin1String("^In (.*) included from (.*):(\\d+):$")), + m_messageRegExp(QLatin1String("^") + QLatin1String(FILE_PATTERN) + QLatin1String("(:(\\d+):\\d+|\\((\\d+)\\) *): +(fatal +)?(error|warning|note): (.*)$")), + m_summaryRegExp(QLatin1String("^\\d+ (warnings?|errors?)( and \\d (warnings?|errors?))? generated.$")), + m_codesignRegExp(QLatin1String("^Code ?Sign error: (.*)$")), + m_expectSnippet(false) +{ + setObjectName(QLatin1String("ClangParser")); + + appendOutputParser(new LdParser); +} + +void ClangParser::stdError(const QString &line) +{ + const QString lne = rightTrimmed(line); + if (m_summaryRegExp.indexIn(lne) > -1) { + doFlush(); + m_expectSnippet = false; + return; + } + + if (m_commandRegExp.indexIn(lne) > -1) { + m_expectSnippet = true; + Task task(Task::Error, + m_commandRegExp.cap(4), + Utils::FileName(), /* filename */ + -1, /* line */ + Constants::TASK_CATEGORY_COMPILE); + if (m_commandRegExp.cap(3) == QLatin1String("warning")) + task.type = Task::Warning; + else if (m_commandRegExp.cap(3) == QLatin1String("note")) + task.type = Task::Unknown; + newTask(task); + return; + } + + if (m_inLineRegExp.indexIn(lne) > -1) { + m_expectSnippet = true; + newTask(Task(Task::Unknown, + lne.trimmed(), + Utils::FileName::fromUserInput(m_inLineRegExp.cap(2)), /* filename */ + m_inLineRegExp.cap(3).toInt(), /* line */ + Constants::TASK_CATEGORY_COMPILE)); + return; + } + + if (m_messageRegExp.indexIn(lne) > -1) { + m_expectSnippet = true; + bool ok = false; + int lineNo = m_messageRegExp.cap(4).toInt(&ok); + if (!ok) + lineNo = m_messageRegExp.cap(5).toInt(&ok); + Task task(Task::Error, + m_messageRegExp.cap(8), + Utils::FileName::fromUserInput(m_messageRegExp.cap(1)), /* filename */ + lineNo, + Core::Id(Constants::TASK_CATEGORY_COMPILE)); + if (m_messageRegExp.cap(7) == QLatin1String("warning")) + task.type = Task::Warning; + else if (m_messageRegExp.cap(7) == QLatin1String("note")) + task.type = Task::Unknown; + newTask(task); + return; + } + + if (m_codesignRegExp.indexIn(lne) > -1) { + m_expectSnippet = true; + Task task(Task::Error, + m_codesignRegExp.cap(1), + Utils::FileName(), + -1, + Core::Id(Constants::TASK_CATEGORY_COMPILE)); + newTask(task); + return; + } + + if (m_expectSnippet) { + amendDescription(lne, true); + return; + } + + IOutputParser::stdError(line); +} + +// Unit tests: + +#ifdef WITH_TESTS_NOTDISABLED +# include + +# include "projectexplorer.h" +# include "metatypedeclarations.h" +# include "outputparser_test.h" + +void ProjectExplorerPlugin::testClangOutputParser_data() +{ + QTest::addColumn("input"); + QTest::addColumn("inputChannel"); + QTest::addColumn("childStdOutLines"); + QTest::addColumn("childStdErrLines"); + QTest::addColumn >("tasks"); + QTest::addColumn("outputLines"); + + const Core::Id categoryCompile = Constants::TASK_CATEGORY_COMPILE; + + QTest::newRow("pass-through stdout") + << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT + << QString::fromLatin1("Sometext\n") << QString() + << QList() + << QString(); + QTest::newRow("pass-through stderr") + << QString::fromLatin1("Sometext") << OutputParserTester::STDERR + << QString() << QString::fromLatin1("Sometext\n") + << QList() + << QString(); + + QTest::newRow("clang++ warning") + << QString::fromLatin1("clang++: warning: argument unused during compilation: '-mthreads'") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Warning, + QLatin1String("argument unused during compilation: '-mthreads'"), + Utils::FileName(), -1, + categoryCompile)) + << QString(); + QTest::newRow("clang++ error") + << QString::fromLatin1("clang++: error: no input files [err_drv_no_input_files]") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Error, + QLatin1String("no input files [err_drv_no_input_files]"), + Utils::FileName(), -1, + categoryCompile)) + << QString(); + QTest::newRow("complex warning") + << QString::fromLatin1("In file included from ..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qnamespace.h:45:\n" + "..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qglobal.h(1425) : warning: unknown attribute 'dllimport' ignored [-Wunknown-attributes]\n" + "class Q_CORE_EXPORT QSysInfo {\n" + " ^") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Unknown, + QLatin1String("In file included from ..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qnamespace.h:45:"), + Utils::FileName::fromUserInput(QLatin1String("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qnamespace.h")), 45, + categoryCompile) + << Task(Task::Warning, + QLatin1String("unknown attribute 'dllimport' ignored [-Wunknown-attributes]\n" + "class Q_CORE_EXPORT QSysInfo {\n" + " ^"), + Utils::FileName::fromUserInput(QLatin1String("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qglobal.h")), 1425, + categoryCompile)) + << QString(); + QTest::newRow("note") + << QString::fromLatin1("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qglobal.h:1289:27: note: instantiated from:\n" + "# define Q_CORE_EXPORT Q_DECL_IMPORT\n" + " ^") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Unknown, + QLatin1String("instantiated from:\n" + "# define Q_CORE_EXPORT Q_DECL_IMPORT\n" + " ^"), + Utils::FileName::fromUserInput(QLatin1String("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qglobal.h")), 1289, + categoryCompile)) + << QString(); + QTest::newRow("fatal error") + << QString::fromLatin1("/usr/include/c++/4.6/utility:68:10: fatal error: 'bits/c++config.h' file not found\n" + "#include \n" + " ^") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Error, + QLatin1String("'bits/c++config.h' file not found\n" + "#include \n" + " ^"), + Utils::FileName::fromUserInput(QLatin1String("/usr/include/c++/4.6/utility")), 68, + categoryCompile)) + << QString(); + + QTest::newRow("line confusion") + << QString::fromLatin1("/home/code/src/creator/src/plugins/coreplugin/manhattanstyle.cpp:567:51: warning: ?: has lower precedence than +; + will be evaluated first [-Wparentheses]\n" + " int x = option->rect.x() + horizontal ? 2 : 6;\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Warning, + QLatin1String("?: has lower precedence than +; + will be evaluated first [-Wparentheses]\n" + " int x = option->rect.x() + horizontal ? 2 : 6;\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^"), + Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/coreplugin/manhattanstyle.cpp")), 567, + categoryCompile)) + << QString(); + QTest::newRow("code sign error") + << QString::fromLatin1("Check dependencies\n" + "Code Sign error: No matching provisioning profiles found: No provisioning profiles with a valid signing identity (i.e. certificate and private key pair) were found.\n" + "CodeSign error: code signing is required for product type 'Application' in SDK 'iOS 7.0'") + << OutputParserTester::STDERR + << QString() << QString::fromLatin1("Check dependencies\n") + << (QList() + << Task(Task::Error, + QLatin1String("No matching provisioning profiles found: No provisioning profiles with a valid signing identity (i.e. certificate and private key pair) were found."), + Utils::FileName(), -1, + categoryCompile) + << Task(Task::Error, + QLatin1String("code signing is required for product type 'Application' in SDK 'iOS 7.0'"), + Utils::FileName(), -1, + categoryCompile)) + << QString(); +} + +void ProjectExplorerPlugin::testClangOutputParser() +{ + OutputParserTester testbench; + testbench.appendOutputParser(new ClangParser); + QFETCH(QString, input); + QFETCH(OutputParserTester::Channel, inputChannel); + QFETCH(QList, tasks); + QFETCH(QString, childStdOutLines); + QFETCH(QString, childStdErrLines); + QFETCH(QString, outputLines); + + testbench.testParsing(input, inputChannel, + tasks, childStdOutLines, childStdErrLines, + outputLines); +} +#endif diff --git a/src/plugins/boostbuildprojectmanager/external/projectexplorer/clangparser.h b/src/plugins/boostbuildprojectmanager/external/projectexplorer/clangparser.h new file mode 100644 index 0000000000..4a914b7326 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/external/projectexplorer/clangparser.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef CLANGPARSER_H +#define CLANGPARSER_H + +#include "gccparser.h" +#include + +#include + +namespace ProjectExplorer { + +class ClangParser : public ProjectExplorer::GccParser +{ + Q_OBJECT + +public: + ClangParser(); + void stdError(const QString &line); + +private: + QRegExp m_commandRegExp; + QRegExp m_inLineRegExp; + QRegExp m_messageRegExp; + QRegExp m_summaryRegExp; + QRegExp m_codesignRegExp; + bool m_expectSnippet; +}; + +} // namespace ProjectExplorer + +#endif // CLANGPARSER_H diff --git a/src/plugins/boostbuildprojectmanager/external/projectexplorer/gccparser.cpp b/src/plugins/boostbuildprojectmanager/external/projectexplorer/gccparser.cpp new file mode 100644 index 0000000000..c6b7e02ba7 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/external/projectexplorer/gccparser.cpp @@ -0,0 +1,880 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License Version 2.1 by the Free Software +** Foundation and appearing in the file LICENSE.txt included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License Version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "gccparser.h" +#include "ldparser.h" +#include +#include + +#include + +using namespace ProjectExplorer; + +// opt. drive letter + filename: (2 brackets) +static const char FILE_PATTERN[] = "(|([A-Za-z]:)?[^:]+):"; +static const char COMMAND_PATTERN[] = "^(.*[\\\\/])?([a-z0-9]+-[a-z0-9]+-[a-z0-9]+-)?(gcc|g\\+\\+)(-[0-9\\.]+)?(\\.exe)?: "; + +GccParser::GccParser() +{ + setObjectName(QLatin1String("GCCParser")); + m_regExp.setPattern(QLatin1Char('^') + QLatin1String(FILE_PATTERN) + + QLatin1String("(\\d+):(\\d+:)?\\s+((fatal |#)?(warning|error|note):?\\s)?([^\\s].+)$")); + m_regExp.setMinimal(true); + QTC_CHECK(m_regExp.isValid()); + + m_regExpIncluded.setPattern(QString::fromLatin1("\\bfrom\\s") + QLatin1String(FILE_PATTERN) + + QLatin1String("(\\d+)(:\\d+)?[,:]?$")); + m_regExpIncluded.setMinimal(true); + QTC_CHECK(m_regExpIncluded.isValid()); + + // optional path with trailing slash + // optional arm-linux-none-thingy + // name of executable + // optional trailing version number + // optional .exe postfix + m_regExpGccNames.setPattern(QLatin1String(COMMAND_PATTERN)); + m_regExpGccNames.setMinimal(true); + QTC_CHECK(m_regExpGccNames.isValid()); + + appendOutputParser(new LdParser); +} + +void GccParser::stdError(const QString &line) +{ + QString lne = rightTrimmed(line); + + // Blacklist some lines to not handle them: + if (lne.startsWith(QLatin1String("TeamBuilder ")) || + lne.startsWith(QLatin1String("distcc["))) { + IOutputParser::stdError(line); + return; + } + + // Handle misc issues: + if (lne.startsWith(QLatin1String("ERROR:")) || + lne == QLatin1String("* cpp failed")) { + newTask(Task(Task::Error, + lne /* description */, + Utils::FileName() /* filename */, + -1 /* linenumber */, + Constants::TASK_CATEGORY_COMPILE)); + return; + } else if (m_regExpGccNames.indexIn(lne) > -1) { + QString description = lne.mid(m_regExpGccNames.matchedLength()); + Task task(Task::Error, + description, + Utils::FileName(), /* filename */ + -1, /* line */ + Constants::TASK_CATEGORY_COMPILE); + if (description.startsWith(QLatin1String("warning: "))) { + task.type = Task::Warning; + task.description = description.mid(9); + } else if (description.startsWith(QLatin1String("fatal: "))) { + task.description = description.mid(7); + } + newTask(task); + return; + } else if (m_regExp.indexIn(lne) > -1) { + Utils::FileName filename = Utils::FileName::fromUserInput(m_regExp.cap(1)); + int lineno = m_regExp.cap(3).toInt(); + Task task(Task::Unknown, + m_regExp.cap(8) /* description */, + filename, lineno, + Constants::TASK_CATEGORY_COMPILE); + if (m_regExp.cap(7) == QLatin1String("warning")) + task.type = Task::Warning; + else if (m_regExp.cap(7) == QLatin1String("error") || + task.description.startsWith(QLatin1String("undefined reference to")) || + task.description.startsWith(QLatin1String("multiple definition of"))) + task.type = Task::Error; + + // Prepend "#warning" or "#error" if that triggered the match on (warning|error) + // We want those to show how the warning was triggered + if (m_regExp.cap(5).startsWith(QLatin1Char('#'))) + task.description = m_regExp.cap(5) + task.description; + + newTask(task); + return; + } else if (m_regExpIncluded.indexIn(lne) > -1) { + newTask(Task(Task::Unknown, + lne.trimmed() /* description */, + Utils::FileName::fromUserInput(m_regExpIncluded.cap(1)) /* filename */, + m_regExpIncluded.cap(3).toInt() /* linenumber */, + Constants::TASK_CATEGORY_COMPILE)); + return; + } else if (lne.startsWith(QLatin1Char(' '))) { + amendDescription(lne, true); + return; + } + + doFlush(); + IOutputParser::stdError(line); +} + +void GccParser::stdOutput(const QString &line) +{ + doFlush(); + IOutputParser::stdOutput(line); +} + +void GccParser::newTask(const Task &task) +{ + doFlush(); + m_currentTask = task; +} + +void GccParser::doFlush() +{ + if (m_currentTask.isNull()) + return; + Task t = m_currentTask; + m_currentTask.clear(); + emit addTask(t); +} + +void GccParser::amendDescription(const QString &desc, bool monospaced) +{ + if (m_currentTask.isNull()) + return; + int start = m_currentTask.description.count() + 1; + m_currentTask.description.append(QLatin1Char('\n')); + m_currentTask.description.append(desc); + if (monospaced) { + QTextLayout::FormatRange fr; + fr.start = start; + fr.length = desc.count() + 1; + fr.format.setFontFamily(QLatin1String("Monospaced")); + fr.format.setFontStyleHint(QFont::TypeWriter); + m_currentTask.formats.append(fr); + } + return; +} + +// Unit tests: + +#ifdef WITH_TESTS_NOTDISABLED +# include + +# include "projectexplorer.h" +# include "metatypedeclarations.h" +# include "outputparser_test.h" + +void ProjectExplorerPlugin::testGccOutputParsers_data() +{ + QTest::addColumn("input"); + QTest::addColumn("inputChannel"); + QTest::addColumn("childStdOutLines"); + QTest::addColumn("childStdErrLines"); + QTest::addColumn >("tasks"); + QTest::addColumn("outputLines"); + + const Core::Id categoryCompile = Constants::TASK_CATEGORY_COMPILE; + + QTest::newRow("pass-through stdout") + << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT + << QString::fromLatin1("Sometext\n") << QString() + << QList() + << QString(); + QTest::newRow("pass-through stderr") + << QString::fromLatin1("Sometext") << OutputParserTester::STDERR + << QString() << QString::fromLatin1("Sometext\n") + << QList() + << QString(); + + QTest::newRow("ar output") + << QString::fromLatin1("../../../../x86/i686-unknown-linux-gnu/bin/i686-unknown-linux-gnu-ar: creating lib/libSkyView.a") << OutputParserTester::STDERR + << QString() << QString::fromLatin1("../../../../x86/i686-unknown-linux-gnu/bin/i686-unknown-linux-gnu-ar: creating lib/libSkyView.a\n") + << QList() + << QString(); + + QTest::newRow("GCCE error") + << QString::fromLatin1("/temp/test/untitled8/main.cpp: In function `int main(int, char**)':\n" + "/temp/test/untitled8/main.cpp:9: error: `sfasdf' undeclared (first use this function)\n" + "/temp/test/untitled8/main.cpp:9: error: (Each undeclared identifier is reported only once for each function it appears in.)") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Unknown, + QLatin1String("In function `int main(int, char**)':"), + Utils::FileName::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), -1, + categoryCompile) + << Task(Task::Error, + QLatin1String("`sfasdf' undeclared (first use this function)"), + Utils::FileName::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), 9, + categoryCompile) + << Task(Task::Error, + QLatin1String("(Each undeclared identifier is reported only once for each function it appears in.)"), + Utils::FileName::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), 9, + categoryCompile) + ) + << QString(); + QTest::newRow("GCCE warning") + << QString::fromLatin1("/src/corelib/global/qglobal.h:1635: warning: inline function `QDebug qDebug()' used but never defined") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Warning, + QLatin1String("inline function `QDebug qDebug()' used but never defined"), + Utils::FileName::fromUserInput(QLatin1String("/src/corelib/global/qglobal.h")), 1635, + categoryCompile)) + << QString(); + QTest::newRow("warning") + << QString::fromLatin1("main.cpp:7:2: warning: Some warning") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() << Task(Task::Warning, + QLatin1String("Some warning"), + Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 7, + categoryCompile)) + << QString(); + QTest::newRow("GCCE #error") + << QString::fromLatin1("C:\\temp\\test\\untitled8\\main.cpp:7: #error Symbian error") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Error, + QLatin1String("#error Symbian error"), + Utils::FileName::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8\\main.cpp")), 7, + categoryCompile)) + << QString(); + // Symbian reports #warning(s) twice (using different syntax). + QTest::newRow("GCCE #warning1") + << QString::fromLatin1("C:\\temp\\test\\untitled8\\main.cpp:8: warning: #warning Symbian warning") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Warning, + QLatin1String("#warning Symbian warning"), + Utils::FileName::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8\\main.cpp")), 8, + categoryCompile)) + << QString(); + QTest::newRow("GCCE #warning2") + << QString::fromLatin1("/temp/test/untitled8/main.cpp:8:2: warning: #warning Symbian warning") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Warning, + QLatin1String("#warning Symbian warning"), + Utils::FileName::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), 8, + categoryCompile)) + << QString(); + QTest::newRow("Undefined reference (debug)") + << QString::fromLatin1("main.o: In function `main':\n" + "C:\\temp\\test\\untitled8/main.cpp:8: undefined reference to `MainWindow::doSomething()'\n" + "collect2: ld returned 1 exit status") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Unknown, + QLatin1String("In function `main':"), + Utils::FileName::fromUserInput(QLatin1String("main.o")), -1, + categoryCompile) + << Task(Task::Error, + QLatin1String("undefined reference to `MainWindow::doSomething()'"), + Utils::FileName::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8/main.cpp")), 8, + categoryCompile) + << Task(Task::Error, + QLatin1String("collect2: ld returned 1 exit status"), + Utils::FileName(), -1, + categoryCompile) + ) + << QString(); + QTest::newRow("Undefined reference (release)") + << QString::fromLatin1("main.o: In function `main':\n" + "C:\\temp\\test\\untitled8/main.cpp:(.text+0x40): undefined reference to `MainWindow::doSomething()'\n" + "collect2: ld returned 1 exit status") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Unknown, + QLatin1String("In function `main':"), + Utils::FileName::fromUserInput(QLatin1String("main.o")), -1, + categoryCompile) + << Task(Task::Error, + QLatin1String("undefined reference to `MainWindow::doSomething()'"), + Utils::FileName::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8/main.cpp")), -1, + categoryCompile) + << Task(Task::Error, + QLatin1String("collect2: ld returned 1 exit status"), + Utils::FileName(), -1, + categoryCompile) + ) + << QString(); + QTest::newRow("linker: dll format not recognized") + << QString::fromLatin1("c:\\Qt\\4.6\\lib/QtGuid4.dll: file not recognized: File format not recognized") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Error, + QLatin1String("file not recognized: File format not recognized"), + Utils::FileName::fromUserInput(QLatin1String("c:\\Qt\\4.6\\lib/QtGuid4.dll")), -1, + categoryCompile)) + << QString(); + QTest::newRow("Invalid rpath") + << QString::fromLatin1("g++: /usr/local/lib: No such file or directory") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Error, + QLatin1String("/usr/local/lib: No such file or directory"), + Utils::FileName(), -1, + categoryCompile)) + << QString(); + + QTest::newRow("Invalid rpath") + << QString::fromLatin1("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp: In member function 'void Debugger::Internal::GdbEngine::handleBreakInsert2(const Debugger::Internal::GdbResponse&)':\n" + "../../../../master/src/plugins/debugger/gdb/gdbengine.cpp:2114: warning: unused variable 'index'\n" + "../../../../master/src/plugins/debugger/gdb/gdbengine.cpp:2115: warning: unused variable 'handler'") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Unknown, + QLatin1String("In member function 'void Debugger::Internal::GdbEngine::handleBreakInsert2(const Debugger::Internal::GdbResponse&)':"), + Utils::FileName::fromUserInput(QLatin1String("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp")), -1, + categoryCompile) + << Task(Task::Warning, + QLatin1String("unused variable 'index'"), + Utils::FileName::fromUserInput(QLatin1String("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp")), 2114, + categoryCompile) + << Task(Task::Warning, + QLatin1String("unused variable 'handler'"), + Utils::FileName::fromUserInput(QLatin1String("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp")), 2115, + categoryCompile)) + << QString(); + QTest::newRow("gnumakeparser.cpp errors") + << QString::fromLatin1("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp: In member function 'void ProjectExplorer::ProjectExplorerPlugin::testGnuMakeParserTaskMangling_data()':\n" + "/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp:264: error: expected primary-expression before ':' token\n" + "/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp:264: error: expected ';' before ':' token") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Unknown, + QLatin1String("In member function 'void ProjectExplorer::ProjectExplorerPlugin::testGnuMakeParserTaskMangling_data()':"), + Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp")), -1, + categoryCompile) + << Task(Task::Error, + QLatin1String("expected primary-expression before ':' token"), + Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp")), 264, + categoryCompile) + << Task(Task::Error, + QLatin1String("expected ';' before ':' token"), + Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp")), 264, + categoryCompile)) + << QString(); + QTest::newRow("distcc error(QTCREATORBUG-904)") + << QString::fromLatin1("distcc[73168] (dcc_get_hostlist) Warning: no hostlist is set; can't distribute work\n" + "distcc[73168] (dcc_build_somewhere) Warning: failed to distribute, running locally instead") + << OutputParserTester::STDERR + << QString() << QString::fromLatin1("distcc[73168] (dcc_get_hostlist) Warning: no hostlist is set; can't distribute work\n" + "distcc[73168] (dcc_build_somewhere) Warning: failed to distribute, running locally instead\n") + << QList() + << QString(); + QTest::newRow("ld warning (QTCREATORBUG-905)") + << QString::fromLatin1("ld: warning: Core::IEditor* QVariant::value() const has different visibility (hidden) in .obj/debug-shared/openeditorsview.o and (default) in .obj/debug-shared/editormanager.o") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Warning, + QLatin1String("Core::IEditor* QVariant::value() const has different visibility (hidden) in .obj/debug-shared/openeditorsview.o and (default) in .obj/debug-shared/editormanager.o"), + Utils::FileName(), -1, + categoryCompile)) + << QString(); + QTest::newRow("ld fatal") + << QString::fromLatin1("ld: fatal: Symbol referencing errors. No output written to testproject") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Error, + QLatin1String("Symbol referencing errors. No output written to testproject"), + Utils::FileName(), -1, + categoryCompile)) + << QString(); + QTest::newRow("Teambuilder issues") + << QString::fromLatin1("TeamBuilder Client:: error: could not find Scheduler, running Job locally...") + << OutputParserTester::STDERR + << QString() << QString::fromLatin1("TeamBuilder Client:: error: could not find Scheduler, running Job locally...\n") + << QList() + << QString(); + QTest::newRow("note") + << QString::fromLatin1("/home/dev/creator/share/qtcreator/debugger/dumper.cpp:1079: note: initialized from here") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("initialized from here"), + Utils::FileName::fromUserInput(QLatin1String("/home/dev/creator/share/qtcreator/debugger/dumper.cpp")), 1079, + categoryCompile)) + << QString(); + QTest::newRow("static member function") + << QString::fromLatin1("/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c: In static member function 'static std::_Rb_tree_node_base* std::_Rb_global<_Dummy>::_Rebalance_for_erase(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*&, std::_Rb_tree_node_base*&, std::_Rb_tree_node_base*&)':\n" + "/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c:194: warning: suggest explicit braces to avoid ambiguous 'else'") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("In static member function 'static std::_Rb_tree_node_base* std::_Rb_global<_Dummy>::_Rebalance_for_erase(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*&, std::_Rb_tree_node_base*&, std::_Rb_tree_node_base*&)':"), + Utils::FileName::fromUserInput(QLatin1String("/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c")), -1, + categoryCompile) + << Task(Task::Warning, + QLatin1String("suggest explicit braces to avoid ambiguous 'else'"), + Utils::FileName::fromUserInput(QLatin1String("/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c")), 194, + categoryCompile)) + << QString(); + QTest::newRow("rm false positive") + << QString::fromLatin1("rm: cannot remove `release/moc_mainwindow.cpp': No such file or directory") + << OutputParserTester::STDERR + << QString() << QString(QLatin1String("rm: cannot remove `release/moc_mainwindow.cpp': No such file or directory\n")) + << QList() + << QString(); + QTest::newRow("ranlib false positive") + << QString::fromLatin1("ranlib: file: libSupport.a(HashTable.o) has no symbols") + << OutputParserTester::STDERR + << QString() << QString(QLatin1String("ranlib: file: libSupport.a(HashTable.o) has no symbols\n")) + << QList() + << QString(); + QTest::newRow("ld: missing library") + << QString::fromLatin1("/usr/bin/ld: cannot find -ldoesnotexist") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Error, + QLatin1String("cannot find -ldoesnotexist"), + Utils::FileName(), -1, + categoryCompile)) + << QString(); + QTest::newRow("In function") + << QString::fromLatin1("../../scriptbug/main.cpp: In function void foo(i) [with i = double]:\n" + "../../scriptbug/main.cpp:22: instantiated from here\n" + "../../scriptbug/main.cpp:8: warning: unused variable c") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("In function void foo(i) [with i = double]:"), + Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), -1, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("instantiated from here"), + Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 22, + categoryCompile) + << Task(Task::Warning, + QLatin1String("unused variable c"), + Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 8, + categoryCompile)) + << QString(); + QTest::newRow("instanciated from here") + << QString::fromLatin1("main.cpp:10: instantiated from here ") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("instantiated from here"), + Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 10, + categoryCompile)) + << QString(); + QTest::newRow("In constructor") + << QString::fromLatin1("/dev/creator/src/plugins/find/basetextfind.h: In constructor 'Find::BaseTextFind::BaseTextFind(QTextEdit*)':") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("In constructor 'Find::BaseTextFind::BaseTextFind(QTextEdit*)':"), + Utils::FileName::fromUserInput(QLatin1String("/dev/creator/src/plugins/find/basetextfind.h")), -1, + categoryCompile)) + << QString(); + + QTest::newRow("At global scope") + << QString::fromLatin1("../../scriptbug/main.cpp: At global scope:\n" + "../../scriptbug/main.cpp: In instantiation of void bar(i) [with i = double]:\n" + "../../scriptbug/main.cpp:8: instantiated from void foo(i) [with i = double]\n" + "../../scriptbug/main.cpp:22: instantiated from here\n" + "../../scriptbug/main.cpp:5: warning: unused parameter v") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("At global scope:"), + Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), -1, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("In instantiation of void bar(i) [with i = double]:"), + Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), -1, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("instantiated from void foo(i) [with i = double]"), + Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 8, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("instantiated from here"), + Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 22, + categoryCompile) + << Task(Task::Warning, + QLatin1String("unused parameter v"), + Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 5, + categoryCompile)) + << QString(); + + QTest::newRow("gcc 4.5 fatal error") + << QString::fromLatin1("/home/code/test.cpp:54:38: fatal error: test.moc: No such file or directory") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Error, + QLatin1String("test.moc: No such file or directory"), + Utils::FileName::fromUserInput(QLatin1String("/home/code/test.cpp")), 54, + categoryCompile)) + << QString(); + + QTest::newRow("QTCREATORBUG-597") + << QString::fromLatin1("debug/qplotaxis.o: In function `QPlotAxis':\n" + "M:\\Development\\x64\\QtPlot/qplotaxis.cpp:26: undefined reference to `vtable for QPlotAxis'\n" + "M:\\Development\\x64\\QtPlot/qplotaxis.cpp:26: undefined reference to `vtable for QPlotAxis'\n" + "collect2: ld returned 1 exit status") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("In function `QPlotAxis':"), + Utils::FileName::fromUserInput(QLatin1String("debug/qplotaxis.o")), -1, + categoryCompile) + << Task(Task::Error, + QLatin1String("undefined reference to `vtable for QPlotAxis'"), + Utils::FileName::fromUserInput(QLatin1String("M:\\Development\\x64\\QtPlot/qplotaxis.cpp")), 26, + categoryCompile) + << Task(Task::Error, + QLatin1String("undefined reference to `vtable for QPlotAxis'"), + Utils::FileName::fromUserInput(QLatin1String("M:\\Development\\x64\\QtPlot/qplotaxis.cpp")), 26, + categoryCompile) + << Task(Task::Error, + QLatin1String("collect2: ld returned 1 exit status"), + Utils::FileName(), -1, + categoryCompile)) + << QString(); + + QTest::newRow("instantiated from here should not be an error") + << QString::fromLatin1("../stl/main.cpp: In member function typename _Vector_base<_Tp, _Alloc>::_Tp_alloc_type::const_reference Vector<_Tp, _Alloc>::at(int) [with _Tp = Point, _Alloc = Allocator]:\n" + "../stl/main.cpp:38: instantiated from here\n" + "../stl/main.cpp:31: warning: returning reference to temporary\n" + "../stl/main.cpp: At global scope:\n" + "../stl/main.cpp:31: warning: unused parameter index") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("In member function typename _Vector_base<_Tp, _Alloc>::_Tp_alloc_type::const_reference Vector<_Tp, _Alloc>::at(int) [with _Tp = Point, _Alloc = Allocator]:"), + Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), -1, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("instantiated from here"), + Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), 38, + categoryCompile) + << Task(Task::Warning, + QLatin1String("returning reference to temporary"), + Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), 31, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("At global scope:"), + Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), -1, + categoryCompile) + << Task(Task::Warning, + QLatin1String("unused parameter index"), + Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), 31, + categoryCompile)) + << QString(); + + QTest::newRow("GCCE from lines") + << QString::fromLatin1("In file included from C:/Symbian_SDK/epoc32/include/e32cmn.h:6792,\n" + " from C:/Symbian_SDK/epoc32/include/e32std.h:25,\n" + "C:/Symbian_SDK/epoc32/include/e32cmn.inl: In member function 'SSecureId::operator const TSecureId&() const':\n" + "C:/Symbian_SDK/epoc32/include/e32cmn.inl:7094: warning: returning reference to temporary") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("In file included from C:/Symbian_SDK/epoc32/include/e32cmn.h:6792,"), + Utils::FileName::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32cmn.h")), 6792, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("from C:/Symbian_SDK/epoc32/include/e32std.h:25,"), + Utils::FileName::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32std.h")), 25, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("In member function 'SSecureId::operator const TSecureId&() const':"), + Utils::FileName::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32cmn.inl")), -1, + categoryCompile) + << Task(Task::Warning, + QLatin1String("returning reference to temporary"), + Utils::FileName::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32cmn.inl")), 7094, + categoryCompile)) + << QString(); + + QTest::newRow("QTCREATORBUG-2206") + << QString::fromLatin1("../../../src/XmlUg/targetdelete.c: At top level:") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("At top level:"), + Utils::FileName::fromUserInput(QLatin1String("../../../src/XmlUg/targetdelete.c")), -1, + categoryCompile)) + << QString(); + + QTest::newRow("GCCE 4: commandline, includes") + << QString::fromLatin1("In file included from /Symbian/SDK/EPOC32/INCLUDE/GCCE/GCCE.h:15,\n" + " from :26:\n" + "/Symbian/SDK/epoc32/include/variant/Symbian_OS.hrh:1134:26: warning: no newline at end of file") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("In file included from /Symbian/SDK/EPOC32/INCLUDE/GCCE/GCCE.h:15,"), + Utils::FileName::fromUserInput(QLatin1String("/Symbian/SDK/EPOC32/INCLUDE/GCCE/GCCE.h")), 15, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("from :26:"), + Utils::FileName::fromUserInput(QLatin1String("")), 26, + categoryCompile) + << Task(Task::Warning, + QLatin1String("no newline at end of file"), + Utils::FileName::fromUserInput(QLatin1String("/Symbian/SDK/epoc32/include/variant/Symbian_OS.hrh")), 1134, + categoryCompile)) + << QString(); + + QTest::newRow("Linker fail (release build)") + << QString::fromLatin1("release/main.o:main.cpp:(.text+0x42): undefined reference to `MainWindow::doSomething()'") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Error, + QLatin1String("undefined reference to `MainWindow::doSomething()'"), + Utils::FileName::fromUserInput(QLatin1String("main.cpp")), -1, + categoryCompile)) + << QString(); + + QTest::newRow("enumeration warning") + << QString::fromLatin1("../../../src/shared/proparser/profileevaluator.cpp: In member function 'ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(const ProString&, const ProStringList&)':\n" + "../../../src/shared/proparser/profileevaluator.cpp:2817:9: warning: case value '0' not in enumerated type 'ProFileEvaluator::Private::TestFunc'") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("In member function 'ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(const ProString&, const ProStringList&)':"), + Utils::FileName::fromUserInput(QLatin1String("../../../src/shared/proparser/profileevaluator.cpp")), -1, + categoryCompile) + << Task(Task::Warning, + QLatin1String("case value '0' not in enumerated type 'ProFileEvaluator::Private::TestFunc'"), + Utils::FileName::fromUserInput(QLatin1String("../../../src/shared/proparser/profileevaluator.cpp")), 2817, + categoryCompile)) + << QString(); + + QTest::newRow("include with line:column info") + << QString::fromLatin1("In file included from :0:0:\n" + "./mw.h:4:0: warning: \"STUPID_DEFINE\" redefined") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("In file included from :0:0:"), + Utils::FileName::fromUserInput(QLatin1String("")), 0, + categoryCompile) + << Task(Task::Warning, + QLatin1String("\"STUPID_DEFINE\" redefined"), + Utils::FileName::fromUserInput(QLatin1String("./mw.h")), 4, + categoryCompile)) + << QString(); + QTest::newRow("instanciation with line:column info") + << QString::fromLatin1("file.h: In function 'void UnitTest::CheckEqual(UnitTest::TestResults&, const Expected&, const Actual&, const UnitTest::TestDetails&) [with Expected = unsigned int, Actual = int]':\n" + "file.cpp:87:10: instantiated from here\n" + "file.h:21:5: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("In function 'void UnitTest::CheckEqual(UnitTest::TestResults&, const Expected&, const Actual&, const UnitTest::TestDetails&) [with Expected = unsigned int, Actual = int]':"), + Utils::FileName::fromUserInput(QLatin1String("file.h")), -1, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("instantiated from here"), + Utils::FileName::fromUserInput(QLatin1String("file.cpp")), 87, + categoryCompile) + << Task(Task::Warning, + QLatin1String("comparison between signed and unsigned integer expressions [-Wsign-compare]"), + Utils::FileName::fromUserInput(QLatin1String("file.h")), 21, + categoryCompile)) + << QString(); + QTest::newRow("linker error") // QTCREATORBUG-3107 + << QString::fromLatin1("cns5k_ins_parser_tests.cpp:(.text._ZN20CNS5kINSParserEngine21DropBytesUntilStartedEP14CircularBufferIhE[CNS5kINSParserEngine::DropBytesUntilStarted(CircularBuffer*)]+0x6d): undefined reference to `CNS5kINSPacket::SOH_BYTE'") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Error, + QLatin1String("undefined reference to `CNS5kINSPacket::SOH_BYTE'"), + Utils::FileName::fromUserInput(QLatin1String("cns5k_ins_parser_tests.cpp")), -1, + categoryCompile)) + << QString(); + + QTest::newRow("uic warning") + << QString::fromLatin1("mainwindow.ui: Warning: The name 'pushButton' (QPushButton) is already in use, defaulting to 'pushButton1'.") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Warning, + QLatin1String("The name 'pushButton' (QPushButton) is already in use, defaulting to 'pushButton1'."), + Utils::FileName::fromUserInput(QLatin1String("mainwindow.ui")), -1, + Constants::TASK_CATEGORY_COMPILE)) + << QString(); + + QTest::newRow("libimf warning") + << QString::fromLatin1("libimf.so: warning: warning: feupdateenv is not implemented and will always fail") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Warning, + QLatin1String("warning: feupdateenv is not implemented and will always fail"), + Utils::FileName::fromUserInput(QLatin1String("libimf.so")), -1, + Constants::TASK_CATEGORY_COMPILE)) + << QString(); + + QTest::newRow("gcc 4.8") + << QString::fromLatin1("In file included from /home/code/src/creator/src/libs/extensionsystem/pluginerrorview.cpp:31:0:\n" + ".uic/ui_pluginerrorview.h:14:25: fatal error: QtGui/QAction: No such file or directory\n" + " #include \n" + " ^") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("In file included from /home/code/src/creator/src/libs/extensionsystem/pluginerrorview.cpp:31:0:"), + Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/libs/extensionsystem/pluginerrorview.cpp")), 31, + categoryCompile) + << Task(Task::Error, + QLatin1String("QtGui/QAction: No such file or directory\n" + " #include \n" + " ^"), + Utils::FileName::fromUserInput(QLatin1String(".uic/ui_pluginerrorview.h")), 14, + categoryCompile)) + << QString(); + + QTest::newRow("qtcreatorbug-9195") + << QString::fromLatin1("In file included from /usr/include/qt4/QtCore/QString:1:0,\n" + " from main.cpp:3:\n" + "/usr/include/qt4/QtCore/qstring.h: In function 'void foo()':\n" + "/usr/include/qt4/QtCore/qstring.h:597:5: error: 'QString::QString(const char*)' is private\n" + "main.cpp:7:22: error: within this context") + << OutputParserTester::STDERR + << QString() << QString() + << ( QList() + << Task(Task::Unknown, + QLatin1String("In file included from /usr/include/qt4/QtCore/QString:1:0,"), + Utils::FileName::fromUserInput(QLatin1String("/usr/include/qt4/QtCore/QString")), 1, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("from main.cpp:3:"), + Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 3, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("In function 'void foo()':"), + Utils::FileName::fromUserInput(QLatin1String("/usr/include/qt4/QtCore/qstring.h")), -1, + categoryCompile) + << Task(Task::Error, + QLatin1String("'QString::QString(const char*)' is private"), + Utils::FileName::fromUserInput(QLatin1String("/usr/include/qt4/QtCore/qstring.h")), 597, + categoryCompile) + << Task(Task::Error, + QLatin1String("within this context"), + Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 7, + categoryCompile)) + << QString(); + + QTest::newRow("ld: Multiple definition error") + << QString::fromLatin1("foo.o: In function `foo()':\n" + "/home/user/test/foo.cpp:2: multiple definition of `foo()'\n" + "bar.o:/home/user/test/bar.cpp:4: first defined here\n" + "collect2: error: ld returned 1 exit status") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Unknown, + QLatin1String("In function `foo()':"), + Utils::FileName::fromUserInput(QLatin1String("foo.o")), -1, + categoryCompile) + << Task(Task::Error, + QLatin1String("multiple definition of `foo()'"), + Utils::FileName::fromUserInput(QLatin1String("/home/user/test/foo.cpp")), 2, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("first defined here"), + Utils::FileName::fromUserInput(QLatin1String("/home/user/test/bar.cpp")), 4, + categoryCompile) + << Task(Task::Error, + QLatin1String("collect2: error: ld returned 1 exit status"), + Utils::FileName(), -1, + categoryCompile) + ) + << QString(); + + QTest::newRow("ld: .data section") + << QString::fromLatin1("foo.o:(.data+0x0): multiple definition of `foo'\n" + "bar.o:(.data+0x0): first defined here\n" + "collect2: error: ld returned 1 exit status") + << OutputParserTester::STDERR + << QString() << QString() + << (QList() + << Task(Task::Error, + QLatin1String("multiple definition of `foo'"), + Utils::FileName::fromUserInput(QLatin1String("foo.o")), -1, + categoryCompile) + << Task(Task::Unknown, + QLatin1String("first defined here"), + Utils::FileName::fromUserInput(QLatin1String("bar.o")), -1, + categoryCompile) + << Task(Task::Error, + QLatin1String("collect2: error: ld returned 1 exit status"), + Utils::FileName(), -1, + categoryCompile) + ) + << QString(); +} + +void ProjectExplorerPlugin::testGccOutputParsers() +{ + OutputParserTester testbench; + testbench.appendOutputParser(new GccParser); + QFETCH(QString, input); + QFETCH(OutputParserTester::Channel, inputChannel); + QFETCH(QList, tasks); + QFETCH(QString, childStdOutLines); + QFETCH(QString, childStdErrLines); + QFETCH(QString, outputLines); + + testbench.testParsing(input, inputChannel, + tasks, childStdOutLines, childStdErrLines, + outputLines); +} +#endif diff --git a/src/plugins/boostbuildprojectmanager/external/projectexplorer/gccparser.h b/src/plugins/boostbuildprojectmanager/external/projectexplorer/gccparser.h new file mode 100644 index 0000000000..db8ce4f2b3 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/external/projectexplorer/gccparser.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef GCCPARSER_H +#define GCCPARSER_H + +#include + +#include + +#include + +namespace ProjectExplorer { + +class GccParser : public ProjectExplorer::IOutputParser +{ + Q_OBJECT + +public: + GccParser(); + + void stdError(const QString &line); + void stdOutput(const QString &line); + +protected: + void newTask(const Task &task); + void doFlush(); + + void amendDescription(const QString &desc, bool monospaced); + +private: + QRegExp m_regExp; + QRegExp m_regExpIncluded; + QRegExp m_regExpGccNames; + + Task m_currentTask; +}; + +} // namespace ProjectExplorer + +#endif // GCCPARSER_H diff --git a/src/plugins/boostbuildprojectmanager/external/projectexplorer/ldparser.cpp b/src/plugins/boostbuildprojectmanager/external/projectexplorer/ldparser.cpp new file mode 100644 index 0000000000..9c3866c121 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/external/projectexplorer/ldparser.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License Version 2.1 by the Free Software +** Foundation and appearing in the file LICENSE.txt included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License Version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "ldparser.h" +#include +#include + +#include + +using namespace ProjectExplorer; + +namespace { + // opt. drive letter + filename: (2 brackets) + const char * const FILE_PATTERN = "(([A-Za-z]:)?[^:]+\\.[^:]+):"; + // line no. or elf segment + offset (1 bracket) + // const char * const POSITION_PATTERN = "(\\d+|\\(\\.[^:]+[+-]0x[a-fA-F0-9]+\\):)"; + const char * const POSITION_PATTERN = "(\\d+|\\(\\..+[+-]0x[a-fA-F0-9]+\\)):"; + const char * const COMMAND_PATTERN = "^(.*[\\\\/])?([a-z0-9]+-[a-z0-9]+-[a-z0-9]+-)?(ld|gold)(-[0-9\\.]+)?(\\.exe)?: "; +} + +LdParser::LdParser() +{ + setObjectName(QLatin1String("LdParser")); + m_regExpLinker.setPattern(QLatin1Char('^') + + QString::fromLatin1(FILE_PATTERN) + QLatin1Char('(') + + QString::fromLatin1(FILE_PATTERN) + QLatin1String(")?(") + + QLatin1String(POSITION_PATTERN) + QLatin1String(")?\\s(.+)$")); + m_regExpLinker.setMinimal(true); + QTC_CHECK(m_regExpLinker.isValid()); + + m_regExpGccNames.setPattern(QLatin1String(COMMAND_PATTERN)); + m_regExpGccNames.setMinimal(true); + QTC_CHECK(m_regExpGccNames.isValid()); +} + +void LdParser::stdError(const QString &line) +{ + QString lne = rightTrimmed(line); + if (lne.startsWith(QLatin1String("TeamBuilder ")) + || lne.startsWith(QLatin1String("distcc[")) + || lne.contains(QLatin1String("ar: creating "))) { + IOutputParser::stdError(line); + return; + } + + if (lne.startsWith(QLatin1String("collect2:"))) { + emit addTask(Task(Task::Error, + lne /* description */, + Utils::FileName() /* filename */, + -1 /* linenumber */, + Constants::TASK_CATEGORY_COMPILE)); + return; + } else if (m_regExpGccNames.indexIn(lne) > -1) { + QString description = lne.mid(m_regExpGccNames.matchedLength()); + Task task(Task::Error, + description, + Utils::FileName(), /* filename */ + -1, /* line */ + Constants::TASK_CATEGORY_COMPILE); + if (description.startsWith(QLatin1String("warning: "))) { + task.type = Task::Warning; + task.description = description.mid(9); + } else if (description.startsWith(QLatin1String("fatal: "))) { + task.description = description.mid(7); + } + emit addTask(task); + return; + } else if (m_regExpLinker.indexIn(lne) > -1) { + bool ok; + int lineno = m_regExpLinker.cap(7).toInt(&ok); + if (!ok) + lineno = -1; + Utils::FileName filename = Utils::FileName::fromUserInput(m_regExpLinker.cap(1)); + const QString sourceFileName = m_regExpLinker.cap(4); + if (!sourceFileName.isEmpty() + && !sourceFileName.startsWith(QLatin1String("(.text")) + && !sourceFileName.startsWith(QLatin1String("(.data"))) { + filename = Utils::FileName::fromUserInput(sourceFileName); + } + QString description = m_regExpLinker.cap(8).trimmed(); + Task task(Task::Error, description, filename, lineno, + Constants::TASK_CATEGORY_COMPILE); + if (description.startsWith(QLatin1String("At global scope")) || + description.startsWith(QLatin1String("At top level")) || + description.startsWith(QLatin1String("instantiated from ")) || + description.startsWith(QLatin1String("In ")) || + description.startsWith(QLatin1String("first defined here"))) { + task.type = Task::Unknown; + } + if (description.startsWith(QLatin1String("warning: "), Qt::CaseInsensitive)) { + task.type = Task::Warning; + task.description = description.mid(9); + } + + emit addTask(task); + return; + } + + IOutputParser::stdError(line); +} diff --git a/src/plugins/boostbuildprojectmanager/external/projectexplorer/ldparser.h b/src/plugins/boostbuildprojectmanager/external/projectexplorer/ldparser.h new file mode 100644 index 0000000000..5038763b14 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/external/projectexplorer/ldparser.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef LDPARSER_H +#define LDPARSER_H + +#include + +#include + +namespace ProjectExplorer { + +class LdParser : public ProjectExplorer::IOutputParser +{ + Q_OBJECT + +public: + LdParser(); + void stdError(const QString &line); + +private: + QRegExp m_regExpLinker; + QRegExp m_regExpGccNames; +}; + +} // namespace ProjectExplorer + +#endif // GCCPARSER_H diff --git a/src/plugins/boostbuildprojectmanager/filesselectionwizardpage.cpp b/src/plugins/boostbuildprojectmanager/filesselectionwizardpage.cpp new file mode 100644 index 0000000000..4a3b8b924c --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/filesselectionwizardpage.cpp @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Mateusz Łoskot +** +** This file, as part of Qt Creator Plugin for Boost.Build, +** was modified to accommodate OpenProjectWizard requirements. +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License Version 2.1 by the Free Software +** Foundation and appearing in the file LICENSE.txt included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License Version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "filesselectionwizardpage.h" + +#include "b2openprojectwizard.h" +#include "b2projectmanagerconstants.h" +#include "selectablefilesmodel.h" + +#include +#include + +#include +#include +#include +#include + +namespace BoostBuildProjectManager { +namespace Internal { + +FilesSelectionWizardPage::FilesSelectionWizardPage(OpenProjectWizardDialog *openProjectWizard, QWidget *parent) + : QWizardPage(parent), m_openProjectWizardDialog(openProjectWizard), m_model(0), m_finished(false) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + + createShowFileFilterControls(layout); + createHideFileFilterControls(layout); + createChooseBaseDirControls(layout); + createApplyButton(layout); + + m_view = new QTreeView; + m_view->setMinimumSize(500, 400); + m_view->setHeaderHidden(true); + m_view->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + m_label = new QLabel; + m_label->setMaximumWidth(500); + + layout->addWidget(m_view); + layout->addWidget(m_label); +} + +void FilesSelectionWizardPage::createHideFileFilterControls(QVBoxLayout *layout) +{ + QHBoxLayout *hbox = new QHBoxLayout; + m_hideFilesFilterLabel = new QLabel; + m_hideFilesFilterLabel->setText(tr("Hide files matching:")); + + m_hideFilesFilterLabel->hide(); + hbox->addWidget(m_hideFilesFilterLabel); + m_hideFilesfilterLineEdit = new QLineEdit; + + const QString filter = Core::ICore::settings()->value(QLatin1String(Constants::HIDE_FILE_FILTER_SETTING), + QLatin1String(Constants::HIDE_FILE_FILTER_DEFAULT)).toString(); + m_hideFilesfilterLineEdit->setText(filter); + m_hideFilesfilterLineEdit->hide(); + hbox->addWidget(m_hideFilesfilterLineEdit); + layout->addLayout(hbox); +} + +void FilesSelectionWizardPage::createShowFileFilterControls(QVBoxLayout *layout) +{ + QHBoxLayout *hbox = new QHBoxLayout; + m_showFilesFilterLabel = new QLabel; + m_showFilesFilterLabel->setText(tr("Show files matching:")); + m_showFilesFilterLabel->hide(); + hbox->addWidget(m_showFilesFilterLabel); + m_showFilesfilterLineEdit = new QLineEdit; + + const QString filter = Core::ICore::settings()->value(QLatin1String(Constants::SHOW_FILE_FILTER_SETTING), + QLatin1String(Constants::SHOW_FILE_FILTER_DEFAULT)).toString(); + m_showFilesfilterLineEdit->setText(filter); + m_showFilesfilterLineEdit->hide(); + hbox->addWidget(m_showFilesfilterLineEdit); + layout->addLayout(hbox); +} + +void FilesSelectionWizardPage::createChooseBaseDirControls(QVBoxLayout *layout) +{ + QHBoxLayout *hbox = new QHBoxLayout; + m_baseDirLabel = new QLabel; + m_baseDirLabel->setText(tr("Base directory:")); + m_baseDirLabel->hide(); + hbox->addWidget(m_baseDirLabel); + + m_lastBaseDir = m_openProjectWizardDialog->path(); + m_baseDirChooser = new Utils::PathChooser; + m_baseDirChooser->setEnabled(true); + m_baseDirChooser->setBaseDirectory(m_lastBaseDir); + m_baseDirChooser->setPath(m_lastBaseDir); + connect(m_baseDirChooser, SIGNAL(changed(QString)), this, SLOT(baseDirectoryChanged())); + hbox->addWidget(m_baseDirChooser); + + layout->addLayout(hbox); +} + +void FilesSelectionWizardPage::createApplyButton(QVBoxLayout *layout) +{ + QHBoxLayout *hbox = new QHBoxLayout; + + QSpacerItem *horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + hbox->addItem(horizontalSpacer); + + m_applyFilterButton = new QPushButton(tr("Apply Filter"), this); + m_applyFilterButton->hide(); + hbox->addWidget(m_applyFilterButton); + layout->addLayout(hbox); + + connect(m_applyFilterButton, SIGNAL(clicked()), this, SLOT(applyFilter())); +} + +void FilesSelectionWizardPage::initializePage() +{ + m_view->setModel(0); + delete m_model; + m_model = new SelectableFilesModel(m_lastBaseDir, this); + connect(m_model, SIGNAL(parsingProgress(QString)), + this, SLOT(parsingProgress(QString))); + connect(m_model, SIGNAL(parsingFinished()), + this, SLOT(parsingFinished())); + m_model->startParsing(); + + m_hideFilesFilterLabel->setVisible(false); + m_hideFilesfilterLineEdit->setVisible(false); + + m_showFilesFilterLabel->setVisible(false); + m_showFilesfilterLineEdit->setVisible(false); + + m_applyFilterButton->setVisible(false); + m_view->setVisible(false); + m_label->setVisible(true); + m_view->setModel(m_model); +} + +void FilesSelectionWizardPage::cleanupPage() +{ + m_model->cancel(); + m_model->waitForFinished(); +} + +void FilesSelectionWizardPage::parsingProgress(const QString &text) +{ + m_label->setText(tr("Generating file list...\n\n%1").arg(text)); +} + +void FilesSelectionWizardPage::parsingFinished() +{ + m_finished = true; + + m_hideFilesFilterLabel->setVisible(true); + m_hideFilesfilterLineEdit->setVisible(true); + + m_showFilesFilterLabel->setVisible(true); + m_showFilesfilterLineEdit->setVisible(true); + + m_applyFilterButton->setVisible(true); + m_view->setVisible(true); + m_label->setVisible(false); + m_view->expand(m_view->model()->index(0,0, QModelIndex())); + emit completeChanged(); + applyFilter(); + // work around qt + m_openProjectWizardDialog->setTitleFormat(m_openProjectWizardDialog->titleFormat()); +} + +bool FilesSelectionWizardPage::isComplete() const +{ + return m_finished; +} + +QStringList FilesSelectionWizardPage::selectedPaths() const +{ + return m_model ? m_model->selectedPaths() : QStringList(); +} + +QStringList FilesSelectionWizardPage::selectedFiles() const +{ + return m_model ? m_model->selectedFiles() : QStringList(); +} + +void FilesSelectionWizardPage::applyFilter() +{ + const QString showFilesFilter = m_showFilesfilterLineEdit->text(); + Core::ICore::settings()->setValue(QLatin1String(Constants::SHOW_FILE_FILTER_SETTING), showFilesFilter); + + const QString hideFilesFilter = m_hideFilesfilterLineEdit->text(); + Core::ICore::settings()->setValue(QLatin1String(Constants::HIDE_FILE_FILTER_SETTING), hideFilesFilter); + + m_model->applyFilter(showFilesFilter, hideFilesFilter); +} + +void FilesSelectionWizardPage::baseDirectoryChanged() +{ + QString const baseDir(m_baseDirChooser->path()); + if (baseDir != m_lastBaseDir && QFileInfo(baseDir).isDir()) + { + m_lastBaseDir = baseDir; + initializePage(); + } +} +} // namespace Internal +} // namespace BoostBuildProjectManager diff --git a/src/plugins/boostbuildprojectmanager/filesselectionwizardpage.h b/src/plugins/boostbuildprojectmanager/filesselectionwizardpage.h new file mode 100644 index 0000000000..fe2aa81950 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/filesselectionwizardpage.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Mateusz Łoskot +** +** This file, as part of Qt Creator Plugin for Boost.Build, +** was modified to accommodate OpenProjectWizard requirements. +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License Version 2.1 by the Free Software +** Foundation and appearing in the file LICENSE.txt included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License Version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef FILESSELECTIONWIZARDPAGE_HPP +#define FILESSELECTIONWIZARDPAGE_HPP + +#include + +QT_BEGIN_NAMESPACE +class QVBoxLayout; +class QLabel; +class QTreeView; +class QLineEdit; +QT_END_NAMESPACE + +namespace Utils { +class PathChooser; +} + +namespace BoostBuildProjectManager { +namespace Internal { + +class OpenProjectWizardDialog; +class SelectableFilesModel; + +// TODO: Submit feature request to Qt Creator to make the FileSelectionWizardPage +// a reusable component. +class FilesSelectionWizardPage : public QWizardPage +{ + Q_OBJECT + +public: + FilesSelectionWizardPage(OpenProjectWizardDialog *openProjectWizard, QWidget *parent = 0); + bool isComplete() const; + void initializePage(); + void cleanupPage(); + QStringList selectedFiles() const; + QStringList selectedPaths() const; + +private slots: + void applyFilter(); + void parsingProgress(const QString &text); + void parsingFinished(); + void baseDirectoryChanged(); + +private: + void createHideFileFilterControls(QVBoxLayout *layout); + void createShowFileFilterControls(QVBoxLayout *layout); + void createChooseBaseDirControls(QVBoxLayout *layout); + void createApplyButton(QVBoxLayout *layout); + + OpenProjectWizardDialog *m_openProjectWizardDialog; + SelectableFilesModel *m_model; + + QLabel *m_hideFilesFilterLabel; + QLineEdit *m_hideFilesfilterLineEdit; + + QLabel *m_showFilesFilterLabel; + QLineEdit *m_showFilesfilterLineEdit; + + QPushButton *m_applyFilterButton; + + QLabel* m_baseDirLabel; + Utils::PathChooser* m_baseDirChooser; + QString m_lastBaseDir; + + QTreeView *m_view; + QLabel *m_label; + bool m_finished; +}; + +} // namespace Internal +} // namespace BoostBuildProjectManager + +#endif // FILESSELECTIONWIZARDPAGE_HPP diff --git a/src/plugins/boostbuildprojectmanager/selectablefilesmodel.cpp b/src/plugins/boostbuildprojectmanager/selectablefilesmodel.cpp new file mode 100644 index 0000000000..45a3e29f5e --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/selectablefilesmodel.cpp @@ -0,0 +1,681 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Mateusz Łoskot +** +** This file, as part of Qt Creator Plugin for Boost.Build, +** was modified to accommodate OpenProjectWizard requirements. +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License Version 2.1 by the Free Software +** Foundation and appearing in the file LICENSE.txt included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License Version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "selectablefilesmodel.h" +#include "b2projectmanagerconstants.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace BoostBuildProjectManager { +namespace Internal { + +SelectableFilesModel::SelectableFilesModel(const QString &baseDir, QObject *parent) + : QAbstractItemModel(parent), m_root(0), m_baseDir(baseDir), m_allFiles(true) +{ + Q_ASSERT(QFileInfo(m_baseDir).isDir()); + + // Dummy tree + m_root = new Tree; + m_root->name = QLatin1String("/"); + m_root->parent = 0; + m_root->fullPath = m_baseDir; + m_root->isDir = true; +} + +void SelectableFilesModel::setInitialMarkedFiles(const QStringList &files) +{ + m_files = files.toSet(); + m_outOfBaseDirFiles.clear(); + QString base = m_baseDir + QLatin1Char('/'); + foreach (const QString &file, m_files) + if (!file.startsWith(base)) + m_outOfBaseDirFiles.append(file); + + m_allFiles = false; +} + +void SelectableFilesModel::init() +{ +} + +void SelectableFilesModel::startParsing() +{ + // Build a tree in a future + m_rootForFuture = new Tree; + m_rootForFuture->name = QLatin1String("/"); + m_rootForFuture->parent = 0; + m_rootForFuture->fullPath = m_baseDir; + m_rootForFuture->isDir = true; + + connect(&m_watcher, SIGNAL(finished()), this, SLOT(buildTreeFinished())); + m_watcher.setFuture(QtConcurrent::run(&SelectableFilesModel::run, this)); +} + +void SelectableFilesModel::run(QFutureInterface &fi) +{ + m_futureCount = 0; + buildTree(m_baseDir, m_rootForFuture, fi); +} + +void SelectableFilesModel::buildTreeFinished() +{ + beginResetModel(); + deleteTree(m_root); + m_root = m_rootForFuture; + m_rootForFuture = 0; + endResetModel(); + emit parsingFinished(); +} + +void SelectableFilesModel::waitForFinished() +{ + m_watcher.waitForFinished(); +} + +void SelectableFilesModel::cancel() +{ + m_watcher.cancel(); +} + +bool SelectableFilesModel::filter(Tree *t) +{ + if (t->isDir) + return false; + if (m_files.contains(t->fullPath)) + return false; + + bool showFilterMatch = false; + + //First loop through show file filters and + //if any of them match, cotinue checking. + foreach (const Glob &g, m_showFilesFilter) { + if (g.isMatch(t->name)) { + showFilterMatch = true; + break; + } + } + + //If none of the "show file" filters match just return + if (!showFilterMatch) + return true; + + foreach (const Glob &g, m_hideFilesFilter) { + if (g.isMatch(t->name)) + return true; + } + + return false; +} + +void SelectableFilesModel::buildTree(const QString &baseDir, Tree *tree, QFutureInterface &fi) +{ + const QFileInfoList fileInfoList = QDir(baseDir).entryInfoList(QDir::Files | + QDir::Dirs | + QDir::NoDotAndDotDot); + bool allChecked = true; + bool allUnchecked = true; + foreach (const QFileInfo &fileInfo, fileInfoList) { + if (m_futureCount % 100) { + emit parsingProgress(fileInfo.absoluteFilePath()); + if (fi.isCanceled()) + return; + } + ++m_futureCount; + if (fileInfo.isDir()) { + if (fileInfo.isSymLink()) + continue; + Tree *t = new Tree; + t->parent = tree; + t->name = fileInfo.fileName(); + t->fullPath = fileInfo.filePath(); + t->isDir = true; + buildTree(fileInfo.filePath(), t, fi); + allChecked &= t->checked == Qt::Checked; + allUnchecked &= t->checked == Qt::Unchecked; + tree->childDirectories.append(t); + } else { + Tree *t = new Tree; + t->parent = tree; + t->name = fileInfo.fileName(); + t->checked = m_allFiles || m_files.contains(fileInfo.absoluteFilePath()) ? Qt::Checked : Qt::Unchecked; + t->fullPath = fileInfo.filePath(); + t->isDir = false; + allChecked &= t->checked == Qt::Checked; + allUnchecked &= t->checked == Qt::Unchecked; + tree->files.append(t); + if (!filter(t)) + tree->visibleFiles.append(t); + } + } + if (tree->childDirectories.isEmpty() && tree->visibleFiles.isEmpty()) + tree->checked = Qt::Unchecked; + else if (allChecked) + tree->checked = Qt::Checked; + else if (allUnchecked) + tree->checked = Qt::Unchecked; + else + tree->checked = Qt::PartiallyChecked; +} + +SelectableFilesModel::~SelectableFilesModel() +{ + deleteTree(m_root); +} + +void SelectableFilesModel::deleteTree(Tree *tree) +{ + foreach (Tree *t, tree->childDirectories) + deleteTree(t); + foreach (Tree *t, tree->files) + deleteTree(t); + delete tree; +} + +int SelectableFilesModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return 1; +} + +int SelectableFilesModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) + return 1; + Tree *parentT = static_cast(parent.internalPointer()); + return parentT->childDirectories.size() + parentT->visibleFiles.size(); +} + +QModelIndex SelectableFilesModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!parent.isValid()) + return createIndex(row, column, m_root); + Tree *parentT = static_cast(parent.internalPointer()); + if (row < parentT->childDirectories.size()) + return createIndex(row, column, parentT->childDirectories.at(row)); + else + return createIndex(row, column, parentT->visibleFiles.at(row - parentT->childDirectories.size())); +} + +QModelIndex SelectableFilesModel::parent(const QModelIndex &child) const +{ + if (!child.isValid()) + return QModelIndex(); + Tree *parent = static_cast(child.internalPointer())->parent; + if (!parent) + return QModelIndex(); + if (!parent->parent) //then the parent is the root + return createIndex(0, 0, parent); + // figure out where the parent is + int pos = parent->parent->childDirectories.indexOf(parent); + if (pos == -1) + pos = parent->parent->childDirectories.size() + parent->parent->visibleFiles.indexOf(parent); + return createIndex(pos, 0, parent); +} + +QVariant SelectableFilesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + Tree *t = static_cast(index.internalPointer()); + if (role == Qt::DisplayRole) + return t->name; + if (role == Qt::CheckStateRole) + return t->checked; + if (role == Qt::DecorationRole) { + if (t->icon.isNull()) + t->icon = Core::FileIconProvider::icon(t->fullPath); + return t->icon; + } + return QVariant(); +} + +bool SelectableFilesModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role == Qt::CheckStateRole) { + // We can do that! + Tree *t = static_cast(index.internalPointer()); + t->checked = Qt::CheckState(value.toInt()); + propagateDown(index); + propagateUp(index); + emit dataChanged(index, index); + } + return false; +} + +void SelectableFilesModel::propagateUp(const QModelIndex &index) +{ + QModelIndex parent = index.parent(); + if (!parent.isValid()) + return; + Tree *parentT = static_cast(parent.internalPointer()); + if (!parentT) + return; + bool allChecked = true; + bool allUnchecked = true; + for (int i = 0; i < parentT->childDirectories.size(); ++i) { + allChecked &= parentT->childDirectories.at(i)->checked == Qt::Checked; + allUnchecked &= parentT->childDirectories.at(i)->checked == Qt::Unchecked; + } + for (int i = 0; i < parentT->visibleFiles.size(); ++i) { + allChecked &= parentT->visibleFiles.at(i)->checked == Qt::Checked; + allUnchecked &= parentT->visibleFiles.at(i)->checked == Qt::Unchecked; + } + Qt::CheckState newState = Qt::PartiallyChecked; + if (parentT->childDirectories.isEmpty() && parentT->visibleFiles.isEmpty()) + newState = Qt::Unchecked; + else if (allChecked) + newState = Qt::Checked; + else if (allUnchecked) + newState = Qt::Unchecked; + if (parentT->checked != newState) { + parentT->checked = newState; + emit dataChanged(parent, parent); + propagateUp(parent); + } +} + +void SelectableFilesModel::propagateDown(const QModelIndex &index) +{ + Tree *t = static_cast(index.internalPointer()); + for (int i = 0; ichildDirectories.size(); ++i) { + t->childDirectories[i]->checked = t->checked; + propagateDown(index.child(i, 0)); + } + for (int i = 0; ifiles.size(); ++i) + t->files[i]->checked = t->checked; + + int rows = rowCount(index); + if (rows) + emit dataChanged(index.child(0, 0), index.child(rows-1, 0)); +} + +Qt::ItemFlags SelectableFilesModel::flags(const QModelIndex &index) const +{ + Q_UNUSED(index); + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; +} + +QStringList SelectableFilesModel::selectedPaths() const +{ + QStringList result; + collectPaths(m_root, &result); + return result; +} + +void SelectableFilesModel::collectPaths(Tree *root, QStringList *result) const +{ + if (root->checked == Qt::Unchecked) + return; + result->append(root->fullPath); + foreach (Tree *t, root->childDirectories) + collectPaths(t, result); +} + +QStringList SelectableFilesModel::selectedFiles() const +{ + QStringList result = m_outOfBaseDirFiles; + collectFiles(m_root, &result); + return result; +} + +QStringList SelectableFilesModel::preservedFiles() const +{ + return m_outOfBaseDirFiles; +} + +void SelectableFilesModel::collectFiles(Tree *root, QStringList *result) const +{ + if (root->checked == Qt::Unchecked) + return; + foreach (Tree *t, root->childDirectories) + collectFiles(t, result); + foreach (Tree *t, root->visibleFiles) + if (t->checked == Qt::Checked) + result->append(t->fullPath); +} + +QList SelectableFilesModel::parseFilter(const QString &filter) +{ + QList result; + QStringList list = filter.split(QLatin1Char(';'), QString::SkipEmptyParts); + foreach (const QString &e, list) { + QString entry = e.trimmed(); + Glob g; + if (entry.indexOf(QLatin1Char('*')) == -1 && entry.indexOf(QLatin1Char('?')) == -1) { + g.mode = Glob::EXACT; + g.matchString = entry; + } else if (entry.startsWith(QLatin1Char('*')) && entry.indexOf(QLatin1Char('*'), 1) == -1 + && entry.indexOf(QLatin1Char('?'), 1) == -1) { + g.mode = Glob::ENDSWITH; + g.matchString = entry.mid(1); + } else { + g.mode = Glob::REGEXP; + g.matchRegexp = QRegExp(entry, Qt::CaseInsensitive, QRegExp::Wildcard); + } + result.append(g); + } + return result; +} + +void SelectableFilesModel::applyFilter(const QString &showFilesfilter, const QString &hideFilesfilter) +{ + m_showFilesFilter = parseFilter(showFilesfilter); + m_hideFilesFilter = parseFilter(hideFilesfilter); + applyFilter(createIndex(0, 0, m_root)); +} + +Qt::CheckState SelectableFilesModel::applyFilter(const QModelIndex &index) +{ + bool allChecked = true; + bool allUnchecked = true; + Tree *t = static_cast(index.internalPointer()); + + for (int i=0; i < t->childDirectories.size(); ++i) { + Qt::CheckState childCheckState = applyFilter(index.child(i, 0)); + if (childCheckState == Qt::Checked) + allUnchecked = false; + else if (childCheckState == Qt::Unchecked) + allChecked = false; + else + allChecked = allUnchecked = false; + } + + int visibleIndex = 0; + int visibleEnd = t->visibleFiles.size(); + int startOfBlock = 0; + + bool removeBlock = false; + // first remove filtered out rows.. + for (;visibleIndex < visibleEnd; ++visibleIndex) { + if (startOfBlock == visibleIndex) { + removeBlock = filter(t->visibleFiles.at(visibleIndex)); + } else if (removeBlock != filter(t->visibleFiles.at(visibleIndex))) { + if (removeBlock) { + beginRemoveRows(index, startOfBlock, visibleIndex - 1); + for (int i=startOfBlock; i < visibleIndex; ++i) + t->visibleFiles[i]->checked = Qt::Unchecked; + t->visibleFiles.erase(t->visibleFiles.begin() + startOfBlock, + t->visibleFiles.begin() + visibleIndex); + endRemoveRows(); + visibleIndex = startOfBlock; // start again at startOfBlock + visibleEnd = t->visibleFiles.size(); + } + removeBlock = filter(t->visibleFiles.at(visibleIndex)); + startOfBlock = visibleIndex; + } + } + if (removeBlock) { + beginRemoveRows(index, startOfBlock, visibleEnd - 1); + for (int i=startOfBlock; i < visibleEnd; ++i) + t->visibleFiles[i]->checked = Qt::Unchecked; + t->visibleFiles.erase(t->visibleFiles.begin() + startOfBlock, + t->visibleFiles.begin() + visibleEnd); + endRemoveRows(); + } + + // Figure out which rows should be visible + QList newRows; + for (int i=0; i < t->files.size(); ++i) + if (!filter(t->files.at(i))) + newRows.append(t->files.at(i)); + // now add them! + startOfBlock = 0; + visibleIndex = 0; + visibleEnd = t->visibleFiles.size(); + int newIndex = 0; + int newEnd = newRows.size(); + while (true) { + while (visibleIndex < visibleEnd && newIndex < newEnd && + t->visibleFiles.at(visibleIndex) == newRows.at(newIndex)) { + ++newIndex; + ++visibleIndex; + } + if (visibleIndex >= visibleEnd || newIndex >= newEnd) + break; + startOfBlock = newIndex; + while (newIndex < newEnd && + t->visibleFiles.at(visibleIndex) != newRows.at(newIndex)) { + ++newIndex; + } + // end of block = newIndex + beginInsertRows(index, visibleIndex, visibleIndex + newIndex-startOfBlock-1); + for (int i= newIndex - 1; i >= startOfBlock; --i) + t->visibleFiles.insert(visibleIndex, newRows.at(i)); + endInsertRows(); + visibleIndex = visibleIndex + newIndex-startOfBlock; + visibleEnd = visibleEnd + newIndex-startOfBlock; + if (newIndex >= newEnd) + break; + } + if (newIndex != newEnd) { + beginInsertRows(index, visibleIndex, visibleIndex + newEnd-newIndex-1); + for (int i=newEnd-1; i >=newIndex; --i) + t->visibleFiles.insert(visibleIndex, newRows.at(i)); + endInsertRows(); + } + + for (int i=0; i < t->visibleFiles.size(); ++i) { + if (t->visibleFiles.at(i)->checked == Qt::Checked) + allUnchecked = false; + else + allChecked = false; + } + + Qt::CheckState newState = Qt::PartiallyChecked; + if (t->childDirectories.isEmpty() && t->visibleFiles.isEmpty()) + newState = Qt::Unchecked; + else if (allChecked) + newState = Qt::Checked; + else if (allUnchecked) + newState = Qt::Unchecked; + if (t->checked != newState) { + t->checked = newState; + emit dataChanged(index, index); + } + + return newState; +} + +////////// +// SelectableFilesDialog +////////// + +SelectableFilesDialog::SelectableFilesDialog(const QString &path, const QStringList files, QWidget *parent) + : QDialog(parent) +{ + QVBoxLayout *layout = new QVBoxLayout(); + setLayout(layout); + setWindowTitle(tr("Edit Files")); + + m_view = new QTreeView(this); + + createShowFileFilterControls(layout); + createHideFileFilterControls(layout); + createApplyButton(layout); + + m_selectableFilesModel = new SelectableFilesModel(path, this); + m_selectableFilesModel->setInitialMarkedFiles(files); + m_view->setModel(m_selectableFilesModel); + m_view->setMinimumSize(500, 400); + m_view->setHeaderHidden(true); + m_view->hide(); + layout->addWidget(m_view); + + m_preservedFiles = new QLabel; + m_preservedFiles->hide(); + layout->addWidget(m_preservedFiles); + + m_progressLabel = new QLabel(this); + m_progressLabel->setMaximumWidth(500); + layout->addWidget(m_progressLabel); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal, this); + buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, SIGNAL(accepted()), + this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), + this, SLOT(reject())); + layout->addWidget(buttonBox); + + connect(m_selectableFilesModel, SIGNAL(parsingProgress(QString)), + this, SLOT(parsingProgress(QString))); + connect(m_selectableFilesModel, SIGNAL(parsingFinished()), + this, SLOT(parsingFinished())); + + m_selectableFilesModel->startParsing(); +} + +void SelectableFilesDialog::createHideFileFilterControls(QVBoxLayout *layout) +{ + QHBoxLayout *hbox = new QHBoxLayout; + m_hideFilesFilterLabel = new QLabel; + m_hideFilesFilterLabel->setText(tr("Hide files matching:")); + m_hideFilesFilterLabel->hide(); + hbox->addWidget(m_hideFilesFilterLabel); + m_hideFilesfilterLineEdit = new QLineEdit; + + const QString filter = Core::ICore::settings()->value(QLatin1String(Constants::HIDE_FILE_FILTER_SETTING), + QLatin1String(Constants::HIDE_FILE_FILTER_DEFAULT)).toString(); + m_hideFilesfilterLineEdit->setText(filter); + m_hideFilesfilterLineEdit->hide(); + hbox->addWidget(m_hideFilesfilterLineEdit); + layout->addLayout(hbox); +} + +void SelectableFilesDialog::createShowFileFilterControls(QVBoxLayout *layout) +{ + QHBoxLayout *hbox = new QHBoxLayout; + m_showFilesFilterLabel = new QLabel; + m_showFilesFilterLabel->setText(tr("Show files matching:")); + m_showFilesFilterLabel->hide(); + hbox->addWidget(m_showFilesFilterLabel); + m_showFilesfilterLineEdit = new QLineEdit; + + const QString filter = Core::ICore::settings()->value(QLatin1String(Constants::SHOW_FILE_FILTER_SETTING), + QLatin1String(Constants::SHOW_FILE_FILTER_DEFAULT)).toString(); + m_showFilesfilterLineEdit->setText(filter); + m_showFilesfilterLineEdit->hide(); + hbox->addWidget(m_showFilesfilterLineEdit); + layout->addLayout(hbox); +} + +void SelectableFilesDialog::createApplyButton(QVBoxLayout *layout) +{ + QHBoxLayout *hbox = new QHBoxLayout; + + QSpacerItem *horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + hbox->addItem(horizontalSpacer); + + m_applyFilterButton = new QPushButton(tr("Apply Filter"), this); + m_applyFilterButton->hide(); + hbox->addWidget(m_applyFilterButton); + layout->addLayout(hbox); + + connect(m_applyFilterButton, SIGNAL(clicked()), this, SLOT(applyFilter())); +} + +SelectableFilesDialog::~SelectableFilesDialog() +{ + m_selectableFilesModel->cancel(); + m_selectableFilesModel->waitForFinished(); +} + +void SelectableFilesDialog::parsingProgress(const QString &fileName) +{ + m_progressLabel->setText(tr("Generating file list...\n\n%1").arg(fileName)); +} + +void SelectableFilesDialog::parsingFinished() +{ + m_hideFilesFilterLabel->show(); + m_hideFilesfilterLineEdit->show(); + + m_showFilesFilterLabel->show(); + m_showFilesfilterLineEdit->show(); + + m_applyFilterButton->show(); + m_view->show(); + m_progressLabel->hide(); + m_view->expand(QModelIndex()); + smartExpand(m_selectableFilesModel->index(0,0, QModelIndex())); + applyFilter(); + const QStringList &preservedFiles = m_selectableFilesModel->preservedFiles(); + if (preservedFiles.isEmpty()) { + m_preservedFiles->hide(); + } else { + m_preservedFiles->show(); + m_preservedFiles->setText(tr("Not showing %n files that are outside of the base directory.\nThese files are preserved.", 0, preservedFiles.count())); + } +} + +void SelectableFilesDialog::smartExpand(const QModelIndex &index) +{ + if (m_view->model()->data(index, Qt::CheckStateRole) == Qt::PartiallyChecked) { + m_view->expand(index); + int rows = m_view->model()->rowCount(index); + for (int i = 0; i < rows; ++i) + smartExpand(index.child(i, 0)); + } +} + +QStringList SelectableFilesDialog::selectedFiles() const +{ + return m_selectableFilesModel->selectedFiles(); +} + +void SelectableFilesDialog::applyFilter() +{ + const QString showFilesFilter = m_showFilesfilterLineEdit->text(); + Core::ICore::settings()->setValue(QLatin1String(Constants::SHOW_FILE_FILTER_SETTING), showFilesFilter); + + const QString hideFilesFilter = m_hideFilesfilterLineEdit->text(); + Core::ICore::settings()->setValue(QLatin1String(Constants::HIDE_FILE_FILTER_SETTING), hideFilesFilter); + + m_selectableFilesModel->applyFilter(showFilesFilter, hideFilesFilter); +} + +} // namespace Internal +} // namespace BoostBuildProjectManager + + diff --git a/src/plugins/boostbuildprojectmanager/selectablefilesmodel.h b/src/plugins/boostbuildprojectmanager/selectablefilesmodel.h new file mode 100644 index 0000000000..1b4b5ef481 --- /dev/null +++ b/src/plugins/boostbuildprojectmanager/selectablefilesmodel.h @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Mateusz Łoskot +** +** This file, as part of Qt Creator Plugin for Boost.Build, +** was modified to accommodate OpenProjectWizard requirements. +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License Version 2.1 by the Free Software +** Foundation and appearing in the file LICENSE.txt included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License Version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef SELECTABLEFILESMODEL_HPP +#define SELECTABLEFILESMODEL_HPP + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QVBoxLayout; +QT_END_NAMESPACE + +namespace BoostBuildProjectManager { +namespace Internal { + +struct Tree +{ + QString name; + Qt::CheckState checked; + bool isDir; + QList childDirectories; + QList files; + QList visibleFiles; + QIcon icon; + QString fullPath; + Tree *parent; +}; + +struct Glob +{ + enum Mode { EXACT, ENDSWITH, REGEXP }; + Mode mode; + QString matchString; + mutable QRegExp matchRegexp; + + bool isMatch(const QString &text) const + { + if (mode == Glob::EXACT) { + if (text == matchString) + return true; + } else if (mode == Glob::ENDSWITH) { + if (text.endsWith(matchString)) + return true; + } else if (mode == Glob::REGEXP) { + if (matchRegexp.exactMatch(text)) + return true; + } + return false; + } +}; + +class SelectableFilesModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + SelectableFilesModel(const QString &baseDir, QObject *parent); + ~SelectableFilesModel(); + + void setInitialMarkedFiles(const QStringList &files); + + int columnCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent) const; + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + QStringList selectedFiles() const; + QStringList selectedPaths() const; + QStringList preservedFiles() const; + + // only call this once + void startParsing(); + void waitForFinished(); + void cancel(); + void applyFilter(const QString &selectFilesfilter, const QString &hideFilesfilter); + +signals: + void parsingFinished(); + void parsingProgress(const QString &filename); + +private slots: + void buildTreeFinished(); + +private: + QList parseFilter(const QString &filter); + Qt::CheckState applyFilter(const QModelIndex &index); + bool filter(Tree *t); + void init(); + void run(QFutureInterface &fi); + void collectFiles(Tree *root, QStringList *result) const; + void collectPaths(Tree *root, QStringList *result) const; + void buildTree(const QString &baseDir, Tree *tree, QFutureInterface &fi); + void deleteTree(Tree *tree); + void propagateUp(const QModelIndex &index); + void propagateDown(const QModelIndex &index); + Tree *m_root; + // Used in the future thread need to all not used after calling startParsing + QString m_baseDir; + QSet m_files; + QStringList m_outOfBaseDirFiles; + QFutureWatcher m_watcher; + Tree *m_rootForFuture; + int m_futureCount; + bool m_allFiles; + + QList m_hideFilesFilter; + QList m_showFilesFilter; +}; + +class SelectableFilesDialog : public QDialog +{ + Q_OBJECT + +public: + SelectableFilesDialog(const QString &path, const QStringList files, QWidget *parent); + ~SelectableFilesDialog(); + QStringList selectedFiles() const; + +private slots: + void applyFilter(); + void parsingProgress(const QString &fileName); + void parsingFinished(); + +private: + void smartExpand(const QModelIndex &index); + void createShowFileFilterControls(QVBoxLayout *layout); + void createHideFileFilterControls(QVBoxLayout *layout); + void createApplyButton(QVBoxLayout *layout); + SelectableFilesModel *m_selectableFilesModel; + + QLabel *m_hideFilesFilterLabel; + QLineEdit *m_hideFilesfilterLineEdit; + + QLabel *m_showFilesFilterLabel; + QLineEdit *m_showFilesfilterLineEdit; + + QPushButton *m_applyFilterButton; + + QTreeView *m_view; + QLabel *m_preservedFiles; + QLabel *m_progressLabel; +}; + +} // namespace Internal +} // namespace BoostBuildProjectManager + +#endif // SELECTABLEFILESMODEL_HPP + diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index b39c61e6b5..2dcc147ddc 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -29,6 +29,7 @@ SUBDIRS = \ designer \ resourceeditor \ genericprojectmanager \ + boostbuildprojectmanager \ qmljseditor \ qmlprojectmanager \ glsleditor \ -- cgit v1.2.1