summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Hunger <tobias.hunger@digia.com>2013-01-30 18:19:31 +0100
committerTobias Hunger <tobias.hunger@digia.com>2013-02-01 13:03:36 +0100
commit5ed1f71529a65f261c01ed3b2044080a9097ac21 (patch)
tree8273a6a26807ce2711e80a4b4ecb8e441238ffba
parentdf1f7796bac9f225a42c31e0d62d04226b7d1b9e (diff)
downloadqt-creator-5ed1f71529a65f261c01ed3b2044080a9097ac21.tar.gz
Initial version of qbs plugin
Change-Id: I0e0ae07affccfeab167303bb976475649a630e65 Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
-rw-r--r--qtcreator.qbs1
-rw-r--r--src/plugins/plugins.pro14
-rw-r--r--src/plugins/qbsprojectmanager/QbsProjectManager.pluginspec.in23
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp345
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildconfiguration.h129
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildconfigurationwidget.cpp87
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildconfigurationwidget.h66
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildstep.cpp486
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildstep.h160
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildstepconfigwidget.ui88
-rw-r--r--src/plugins/qbsprojectmanager/qbscleanstep.cpp397
-rw-r--r--src/plugins/qbsprojectmanager/qbscleanstep.h150
-rw-r--r--src/plugins/qbsprojectmanager/qbscleanstepconfigwidget.ui65
-rw-r--r--src/plugins/qbsprojectmanager/qbslogsink.cpp131
-rw-r--r--src/plugins/qbsprojectmanager/qbslogsink.h61
-rw-r--r--src/plugins/qbsprojectmanager/qbsnodes.cpp462
-rw-r--r--src/plugins/qbsprojectmanager/qbsnodes.h169
-rw-r--r--src/plugins/qbsprojectmanager/qbsparser.cpp66
-rw-r--r--src/plugins/qbsprojectmanager/qbsparser.h61
-rw-r--r--src/plugins/qbsprojectmanager/qbsproject.cpp655
-rw-r--r--src/plugins/qbsprojectmanager/qbsproject.h142
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectfile.cpp112
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectfile.h71
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectmanager.cpp231
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectmanager.h87
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectmanager.pro53
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectmanager.qbs82
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectmanager_dependencies.pri4
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectmanager_global.h41
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectmanagerconstants.h67
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp240
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.h93
32 files changed, 4839 insertions, 0 deletions
diff --git a/qtcreator.qbs b/qtcreator.qbs
index 4413f66cae..07e40f2157 100644
--- a/qtcreator.qbs
+++ b/qtcreator.qbs
@@ -59,6 +59,7 @@ Project {
"src/plugins/mercurial/mercurial.qbs",
"src/plugins/perforce/perforce.qbs",
"src/plugins/projectexplorer/projectexplorer.qbs",
+ "src/plugins/qbsprojectmanager/qbsprojectmanager.qbs",
// "src/plugins/qmldesigner/qmldesigner.qbs",
"src/plugins/qmljseditor/qmljseditor.qbs",
"src/plugins/qmljstools/qmljstools.qbs",
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index e82b02806f..1cdbf27578 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -47,6 +47,14 @@ SUBDIRS = plugin_coreplugin \
plugin_todo \
plugin_qnx
+# Look for qbs in the environment...
+isEmpty(QBS_SOURCE_DIR): QBS_SOURCE_DIR = $$(QBS_SOURCE_DIR)
+isEmpty(QBS_BUILD_DIR): QBS_BUILD_DIR = $$(QBS_BUILD_DIR)
+
+!isEmpty(QBS_SOURCE_DIR):!isEmpty(QBS_BUILD_DIR) {
+ SUBDIRS += plugin_qbsprojectmanager
+}
+
isEmpty(IDE_PACKAGE_MODE) {
SUBDIRS += plugin_helloworld \
plugin_updateinfo
@@ -332,6 +340,12 @@ plugin_qnx.depends += plugin_qt4projectmanager
plugin_qnx.depends += plugin_coreplugin
plugin_qnx.depends += plugin_texteditor
+plugin_qbsprojectmanager.subdir = qbsprojectmanager
+plugin_qbsprojectmanager.depends = plugin_texteditor
+plugin_qbsprojectmanager.depends += plugin_projectexplorer
+plugin_qbsprojectmanager.depends += plugin_cpptools
+plugin_qbsprojectmanager.depends += plugin_qtsupport
+
plugin_clearcase.subdir = clearcase
plugin_clearcase.depends = plugin_vcsbase
plugin_clearcase.depends += plugin_projectexplorer
diff --git a/src/plugins/qbsprojectmanager/QbsProjectManager.pluginspec.in b/src/plugins/qbsprojectmanager/QbsProjectManager.pluginspec.in
new file mode 100644
index 0000000000..cb74e7bd29
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/QbsProjectManager.pluginspec.in
@@ -0,0 +1,23 @@
+<plugin name=\"QbsProjectManager\" version=\"$$QTCREATOR_VERSION\" compatVersion=\"$$QTCREATOR_VERSION\" experimental=\"true\">
+ <vendor>Digia Plc</vendor>
+ <copyright>(C) 2013 Digia Plc</copyright>
+ <license>
+Commercial Usage
+
+Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Digia.
+
+GNU Lesser General Public License Usage
+
+Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. 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.
+ </license>
+ <category>Build Systems</category>
+ <description>QBS support</description>
+ <url>http://www.qt-project.org</url>
+ <dependencyList>
+ <dependency name=\"TextEditor\" version=\"$$QTCREATOR_VERSION\"/>
+ <dependency name=\"ProjectExplorer\" version=\"$$QTCREATOR_VERSION\"/>
+ <dependency name=\"CppTools\" version=\"$$QTCREATOR_VERSION\"/>
+ <dependency name=\"QtSupport\" version=\"$$QTCREATOR_VERSION\"/>
+ <dependency name=\"QmlJsTools\" version=\"$$QTCREATOR_VERSION\"/>
+ </dependencyList>
+</plugin>
diff --git a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp
new file mode 100644
index 0000000000..68d3479050
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp
@@ -0,0 +1,345 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "qbsbuildconfiguration.h"
+
+#include "qbsbuildconfigurationwidget.h"
+#include "qbsbuildstep.h"
+#include "qbscleanstep.h"
+#include "qbsproject.h"
+#include "qbsprojectmanagerconstants.h"
+
+#include <utils/qtcassert.h>
+#include <projectexplorer/buildsteplist.h>
+#include <projectexplorer/kit.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
+#include <projectexplorer/toolchain.h>
+
+#include <QInputDialog>
+
+static const char QBS_BC_ID[] = "Qbs.QbsBuildConfiguration";
+static const char QBS_BUILD_DIRECTORY_KEY[] = "Qbs.BuildDirectory";
+
+namespace QbsProjectManager {
+namespace Internal {
+
+// ---------------------------------------------------------------------------
+// QbsBuildConfiguration:
+// ---------------------------------------------------------------------------
+
+QbsBuildConfiguration::QbsBuildConfiguration(ProjectExplorer::Target *target) :
+ BuildConfiguration(target, Core::Id(QBS_BC_ID)),
+ m_isParsing(true),
+ m_parsingError(false)
+{
+ connect(project(), SIGNAL(projectParsingStarted()), this, SIGNAL(enabledChanged()));
+ connect(project(), SIGNAL(projectParsingDone(bool)), this, SIGNAL(enabledChanged()));
+
+ connect(this, SIGNAL(buildDirectoryChanged()), target, SLOT(onBuildDirectoryChanged()));
+ ProjectExplorer::BuildStepList *bsl
+ = stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD));
+ connect(bsl, SIGNAL(stepInserted(int)), this, SLOT(buildStepInserted(int)));
+}
+
+QbsBuildConfiguration::QbsBuildConfiguration(ProjectExplorer::Target *target, const Core::Id id) :
+ BuildConfiguration(target, id)
+{ }
+
+QbsBuildConfiguration::QbsBuildConfiguration(ProjectExplorer::Target *target, QbsBuildConfiguration *source) :
+ BuildConfiguration(target, source),
+ m_buildDirectory(source->m_buildDirectory)
+{
+ cloneSteps(source);
+}
+
+QVariantMap QbsBuildConfiguration::toMap() const
+{
+ QVariantMap map(BuildConfiguration::toMap());
+ map.insert(QLatin1String(QBS_BUILD_DIRECTORY_KEY), m_buildDirectory.toUserOutput());
+ return map;
+}
+
+bool QbsBuildConfiguration::fromMap(const QVariantMap &map)
+{
+ if (!BuildConfiguration::fromMap(map))
+ return false;
+
+ ProjectExplorer::BuildStepList *bsl
+ = stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD));
+ // Fix up the existing build steps:
+ for (int i = 0; i < bsl->count(); ++i) {
+ QbsBuildStep *bs = qobject_cast<QbsBuildStep *>(bsl->at(i));
+ if (bs)
+ connect(bs, SIGNAL(qbsConfigurationChanged()), this, SIGNAL(qbsConfigurationChanged()));
+ }
+
+ m_buildDirectory = Utils::FileName::fromUserInput(map.value(QLatin1String(QBS_BUILD_DIRECTORY_KEY)).toString());
+
+ return true;
+}
+
+void QbsBuildConfiguration::buildStepInserted(int pos)
+{
+ QbsBuildStep *step = qobject_cast<QbsBuildStep *>(stepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD)->at(pos));
+ if (step) {
+ connect(step, SIGNAL(qbsConfigurationChanged()), this, SIGNAL(qbsConfigurationChanged()));
+ emit qbsConfigurationChanged();
+ }
+}
+
+ProjectExplorer::NamedWidget *QbsBuildConfiguration::createConfigWidget()
+{
+ return new QbsBuildConfigurationWidget(this);
+}
+
+QbsBuildStep *QbsBuildConfiguration::qbsStep() const
+{
+ foreach (ProjectExplorer::BuildStep *bs,
+ stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD))->steps()) {
+ if (QbsBuildStep *qbsBs = qobject_cast<QbsBuildStep *>(bs))
+ return qbsBs;
+ }
+ return 0;
+}
+
+QVariantMap QbsBuildConfiguration::qbsConfiguration() const
+{
+ QVariantMap config;
+ QbsBuildStep *qbsBs = qbsStep();
+ if (qbsBs)
+ config = qbsBs->qbsConfiguration();
+ return config;
+}
+
+QString QbsBuildConfiguration::buildDirectory() const
+{
+ QString path = QDir::cleanPath(environment().expandVariables(m_buildDirectory.toString()));
+ return QDir::cleanPath(QDir(target()->project()->projectDirectory()).absoluteFilePath(path));
+}
+
+Internal::QbsProject *QbsBuildConfiguration::project() const
+{
+ return qobject_cast<Internal::QbsProject *>(target()->project());
+}
+
+ProjectExplorer::IOutputParser *QbsBuildConfiguration::createOutputParser() const
+{
+ ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(target()->kit());
+ return tc ? tc->outputParser() : 0;
+}
+
+bool QbsBuildConfiguration::isEnabled() const
+{
+ return !project()->isParsing() && project()->hasParseResult();
+}
+
+QString QbsBuildConfiguration::disabledReason() const
+{
+ if (project()->isParsing())
+ return tr("Parsing the Qbs project.");
+ if (!project()->hasParseResult())
+ return tr("Parsing of Qbs project has failed.");
+ return QString();
+}
+
+ProjectExplorer::BuildConfiguration::BuildType QbsBuildConfiguration::buildType() const
+{
+ QString variant;
+ if (qbsStep())
+ variant = qbsStep()->buildVariant();
+
+ if (variant == QLatin1String(Constants::QBS_VARIANT_DEBUG))
+ return Debug;
+ if (variant == QLatin1String(Constants::QBS_VARIANT_RELEASE))
+ return Release;
+ return Unknown;
+}
+
+void QbsBuildConfiguration::setChangedFiles(const QStringList &files)
+{
+ m_changedFiles = files;
+}
+
+QStringList QbsBuildConfiguration::changedFiles() const
+{
+ return m_changedFiles;
+}
+
+QbsBuildConfiguration *QbsBuildConfiguration::setup(ProjectExplorer::Target *t,
+ const QString &defaultDisplayName,
+ const QString &displayName,
+ const QVariantMap &buildData,
+ const Utils::FileName &directory)
+{
+ // Add the build configuration.
+ QbsBuildConfiguration *bc = new QbsBuildConfiguration(t);
+ bc->setDefaultDisplayName(defaultDisplayName);
+ bc->setDisplayName(displayName);
+ bc->setBuildDirectory(directory);
+
+ ProjectExplorer::BuildStepList *buildSteps
+ = bc->stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD));
+ QbsBuildStep *bs = new QbsBuildStep(buildSteps);
+ bs->setQbsConfiguration(buildData);
+ buildSteps->insertStep(0, bs);
+
+ ProjectExplorer::BuildStepList *cleanSteps
+ = bc->stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_CLEAN));
+ QbsCleanStep *cs = new QbsCleanStep(cleanSteps);
+ cleanSteps->insertStep(0, cs);
+
+ return bc;
+}
+
+void QbsBuildConfiguration::setBuildDirectory(const Utils::FileName &dir)
+{
+ if (m_buildDirectory == dir)
+ return;
+ m_buildDirectory = dir;
+ emit buildDirectoryChanged();
+}
+
+// ---------------------------------------------------------------------------
+// QbsBuildConfigurationFactory:
+// ---------------------------------------------------------------------------
+
+QbsBuildConfigurationFactory::QbsBuildConfigurationFactory(QObject *parent) :
+ IBuildConfigurationFactory(parent)
+{ }
+
+QbsBuildConfigurationFactory::~QbsBuildConfigurationFactory()
+{ }
+
+bool QbsBuildConfigurationFactory::canHandle(const ProjectExplorer::Target *t) const
+{
+ return qobject_cast<Internal::QbsProject *>(t->project());
+}
+
+QList<Core::Id> QbsBuildConfigurationFactory::availableCreationIds(const ProjectExplorer::Target *parent) const
+{
+ if (!canHandle(parent))
+ return QList<Core::Id>();
+ return QList<Core::Id>() << Core::Id(QBS_BC_ID);
+}
+
+QString QbsBuildConfigurationFactory::displayNameForId(const Core::Id id) const
+{
+ if (id == QBS_BC_ID)
+ return tr("Qbs based build");
+ return QString();
+}
+
+bool QbsBuildConfigurationFactory::canCreate(const ProjectExplorer::Target *parent, const Core::Id id) const
+{
+ if (!canHandle(parent))
+ return false;
+ return id == QBS_BC_ID;
+}
+
+ProjectExplorer::BuildConfiguration *QbsBuildConfigurationFactory::create(ProjectExplorer::Target *parent,
+ const Core::Id id,
+ const QString &name)
+{
+ if (!canCreate(parent, id))
+ return 0;
+
+ Internal::QbsProject *project = static_cast<Internal::QbsProject *>(parent->project());
+
+ bool ok = true;
+ QString buildConfigurationName = name;
+ if (buildConfigurationName.isNull())
+ buildConfigurationName = QInputDialog::getText(0,
+ tr("New Configuration"),
+ tr("New configuration name:"),
+ QLineEdit::Normal,
+ QString(), &ok);
+ buildConfigurationName = buildConfigurationName.trimmed();
+ if (!ok || buildConfigurationName.isEmpty())
+ return 0;
+
+ //: Debug build configuration. We recommend not translating it.
+ QString firstName = tr("%1 Debug").arg(buildConfigurationName).trimmed();
+
+ //: Release build configuration. We recommend not translating it.
+ QString secondName = tr("%1 Release").arg(buildConfigurationName).trimmed();
+
+ QVariantMap configData;
+ configData.insert(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY),
+ QLatin1String(Constants::QBS_VARIANT_DEBUG));
+
+ ProjectExplorer::BuildConfiguration *bc
+ = QbsBuildConfiguration::setup(parent, firstName, firstName,
+ configData, project->defaultBuildDirectory());
+ configData.insert(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY),
+ QLatin1String(Constants::QBS_VARIANT_RELEASE));
+ parent->addBuildConfiguration(
+ QbsBuildConfiguration::setup(parent, secondName, secondName,
+ configData, project->defaultBuildDirectory()));
+ return bc;
+}
+
+bool QbsBuildConfigurationFactory::canClone(const ProjectExplorer::Target *parent,
+ ProjectExplorer::BuildConfiguration *source) const
+{
+ return canHandle(parent) && qobject_cast<QbsBuildConfiguration *>(source);
+}
+
+ProjectExplorer::BuildConfiguration *QbsBuildConfigurationFactory::clone(ProjectExplorer::Target *parent,
+ ProjectExplorer::BuildConfiguration *source)
+{
+ if (!canClone(parent, source))
+ return 0;
+ QbsBuildConfiguration *oldbc(static_cast<QbsBuildConfiguration *>(source));
+ return new QbsBuildConfiguration(parent, oldbc);
+}
+
+bool QbsBuildConfigurationFactory::canRestore(const ProjectExplorer::Target *parent,
+ const QVariantMap &map) const
+{
+ if (!canHandle(parent))
+ return false;
+ return ProjectExplorer::idFromMap(map) == Core::Id(QBS_BC_ID);
+}
+
+ProjectExplorer::BuildConfiguration *QbsBuildConfigurationFactory::restore(ProjectExplorer::Target *parent,
+ const QVariantMap &map)
+{
+ if (!canRestore(parent, map))
+ return 0;
+ QbsBuildConfiguration *bc = new QbsBuildConfiguration(parent);
+ if (bc->fromMap(map))
+ return bc;
+ delete bc;
+ return 0;
+}
+
+} // namespace Internal
+} // namespace QbsProjectManager
diff --git a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.h b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.h
new file mode 100644
index 0000000000..8a0749c225
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** 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 QBSBUILDCONFIGURATION_H
+#define QBSBUILDCONFIGURATION_H
+
+#include "qbsprojectmanager_global.h"
+
+#include <projectexplorer/buildconfiguration.h>
+#include <qtsupport/baseqtversion.h>
+
+namespace ProjectExplorer { class FileNode; }
+
+namespace QbsProjectManager {
+
+namespace Internal {
+
+class QbsBuildConfigurationFactory;
+class QbsBuildConfigurationWidget;
+class QbsBuildStep;
+class QbsProject;
+
+class QbsBuildConfiguration : public ProjectExplorer::BuildConfiguration
+{
+ Q_OBJECT
+
+public:
+ explicit QbsBuildConfiguration(ProjectExplorer::Target *target);
+
+ ProjectExplorer::NamedWidget *createConfigWidget();
+
+ QbsBuildStep *qbsStep() const;
+ QVariantMap qbsConfiguration() const;
+ QString buildDirectory() const;
+
+ Internal::QbsProject *project() const;
+
+ QVariantMap toMap() const;
+
+ ProjectExplorer::IOutputParser *createOutputParser() const;
+
+ bool isEnabled() const;
+ QString disabledReason() const;
+
+ BuildType buildType() const;
+
+ void setChangedFiles(const QStringList &files);
+ QStringList changedFiles() const;
+
+signals:
+ void qbsConfigurationChanged();
+
+protected:
+ QbsBuildConfiguration(ProjectExplorer::Target *target, QbsBuildConfiguration *source);
+ QbsBuildConfiguration(ProjectExplorer::Target *target, const Core::Id id);
+ bool fromMap(const QVariantMap &map);
+
+private slots:
+ void buildStepInserted(int pos);
+
+private:
+ static QbsBuildConfiguration *setup(ProjectExplorer::Target *t,
+ const QString &defaultDisplayName,
+ const QString &displayName,
+ const QVariantMap &buildData,
+ const Utils::FileName &directory);
+ void setBuildDirectory(const Utils::FileName &dir);
+
+ bool m_isParsing;
+ bool m_parsingError;
+ Utils::FileName m_buildDirectory;
+ QStringList m_changedFiles;
+
+ friend class QbsBuildConfigurationFactory;
+ friend class QbsBuildConfigurationWidget;
+};
+
+class QbsBuildConfigurationFactory : public ProjectExplorer::IBuildConfigurationFactory
+{
+ Q_OBJECT
+
+public:
+ explicit QbsBuildConfigurationFactory(QObject *parent = 0);
+ ~QbsBuildConfigurationFactory();
+
+ QList<Core::Id> availableCreationIds(const ProjectExplorer::Target *parent) const;
+ QString displayNameForId(const Core::Id id) const;
+
+ bool canCreate(const ProjectExplorer::Target *parent, const Core::Id id) const;
+ ProjectExplorer::BuildConfiguration *create(ProjectExplorer::Target *parent, const Core::Id id, const QString &name = QString());
+ bool canClone(const ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *source) const;
+ ProjectExplorer::BuildConfiguration *clone(ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *source);
+ bool canRestore(const ProjectExplorer::Target *parent, const QVariantMap &map) const;
+ ProjectExplorer::BuildConfiguration *restore(ProjectExplorer::Target *parent, const QVariantMap &map);
+
+private:
+ bool canHandle(const ProjectExplorer::Target *t) const;
+};
+
+} // namespace Internal
+} // namespace QbsProjectManager
+
+#endif // QBSBUILDCONFIGURATION_H
diff --git a/src/plugins/qbsprojectmanager/qbsbuildconfigurationwidget.cpp b/src/plugins/qbsprojectmanager/qbsbuildconfigurationwidget.cpp
new file mode 100644
index 0000000000..9cda2c6138
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsbuildconfigurationwidget.cpp
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "qbsbuildconfigurationwidget.h"
+
+#include "qbsbuildconfiguration.h"
+
+#include <utils/detailswidget.h>
+#include <utils/pathchooser.h>
+
+#include <QGridLayout>
+#include <QLabel>
+#include <QWidget>
+
+namespace QbsProjectManager {
+namespace Internal {
+
+QbsBuildConfigurationWidget::QbsBuildConfigurationWidget(QbsProjectManager::Internal::QbsBuildConfiguration *bc) :
+ m_buildConfiguration(bc),
+ m_ignoreChange(false)
+{
+ connect(m_buildConfiguration, SIGNAL(buildDirectoryChanged()),
+ this, SLOT(buildDirectoryChanged()));
+
+ QVBoxLayout *vbox = new QVBoxLayout(this);
+ vbox->setMargin(0);
+ Utils::DetailsWidget *container = new Utils::DetailsWidget(this);
+ container->setState(Utils::DetailsWidget::NoSummary);
+ vbox->addWidget(container);
+
+ QWidget *details = new QWidget(container);
+ container->setWidget(details);
+
+ QGridLayout *layout = new QGridLayout(details);
+ layout->addWidget(new QLabel(tr("Build directory:"), 0, 0));
+
+ m_buildDirChooser = new Utils::PathChooser;
+ m_buildDirChooser->setExpectedKind(Utils::PathChooser::Directory);
+ layout->addWidget(m_buildDirChooser, 0, 1);
+
+ connect(m_buildDirChooser, SIGNAL(changed(QString)), this, SLOT(buildDirEdited()));
+
+ buildDirectoryChanged();
+}
+
+void QbsBuildConfigurationWidget::buildDirEdited()
+{
+ m_ignoreChange = true;
+ m_buildConfiguration->setBuildDirectory(m_buildDirChooser->fileName());
+}
+
+void QbsBuildConfigurationWidget::buildDirectoryChanged()
+{
+ if (m_ignoreChange)
+ return;
+
+ m_buildDirChooser->setPath(m_buildConfiguration->buildDirectory());
+}
+
+} // namespace Internal
+} // namespace QbsProjectManager
diff --git a/src/plugins/qbsprojectmanager/qbsbuildconfigurationwidget.h b/src/plugins/qbsprojectmanager/qbsbuildconfigurationwidget.h
new file mode 100644
index 0000000000..0625e5ae39
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsbuildconfigurationwidget.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** 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 QBSBUILDCONFIGURATIONWIDGET_H
+#define QBSBUILDCONFIGURATIONWIDGET_H
+
+#include <projectexplorer/namedwidget.h>
+
+namespace Utils {
+class DetailsWidget;
+class PathChooser;
+} // namespace Utils
+
+namespace QbsProjectManager {
+namespace Internal {
+class QbsBuildConfiguration;
+
+class QbsBuildConfigurationWidget : public ProjectExplorer::NamedWidget
+{
+ Q_OBJECT
+
+public:
+ QbsBuildConfigurationWidget(Internal::QbsBuildConfiguration *bc);
+
+private slots:
+ void buildDirEdited();
+
+ // Changes triggered from creator
+ void buildDirectoryChanged();
+
+private:
+ Internal::QbsBuildConfiguration *m_buildConfiguration;
+ Utils::PathChooser *m_buildDirChooser;
+ bool m_ignoreChange;
+};
+
+} // namespace Internal
+} // namespace QbsProjectManager
+
+#endif // QBSBUILDCONFIGURATIONWIDGET_H
diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp
new file mode 100644
index 0000000000..c32ec59158
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp
@@ -0,0 +1,486 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "qbsbuildstep.h"
+
+#include "qbsbuildconfiguration.h"
+#include "qbsparser.h"
+#include "qbsproject.h"
+#include "qbsprojectmanagerconstants.h"
+
+#include "ui_qbsbuildstepconfigwidget.h"
+
+#include <projectexplorer/buildsteplist.h>
+#include <projectexplorer/kit.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
+#include <utils/qtcassert.h>
+
+#include <qbs.h>
+
+static const char QBS_CONFIG[] = "Qbs.Configuration";
+static const char QBS_DRY_RUN[] = "Qbs.DryRun";
+static const char QBS_KEEP_GOING[] = "Qbs.DryKeepGoing";
+static const char QBS_MAXJOBCOUNT[] = "Qbs.MaxJobs";
+
+// --------------------------------------------------------------------
+// Constants:
+// --------------------------------------------------------------------
+
+namespace QbsProjectManager {
+namespace Internal {
+
+// --------------------------------------------------------------------
+// QbsBuildStep:
+// --------------------------------------------------------------------
+
+QbsBuildStep::QbsBuildStep(ProjectExplorer::BuildStepList *bsl) :
+ ProjectExplorer::BuildStep(bsl, Core::Id(Constants::QBS_BUILDSTEP_ID)),
+ m_job(0), m_showCompilerOutput(true), m_parser(0)
+{
+ setDisplayName(tr("qbs build"));
+}
+
+QbsBuildStep::QbsBuildStep(ProjectExplorer::BuildStepList *bsl, const QbsBuildStep *other) :
+ ProjectExplorer::BuildStep(bsl, Core::Id(Constants::QBS_BUILDSTEP_ID)),
+ m_qbsBuildOptions(other->m_qbsBuildOptions), m_job(0),
+ m_showCompilerOutput(other->m_showCompilerOutput), m_parser(0)
+{ }
+
+QbsBuildStep::~QbsBuildStep()
+{
+ cancel();
+ m_job->deleteLater();
+ m_job = 0;
+ delete m_parser;
+}
+
+bool QbsBuildStep::init()
+{
+ if (static_cast<QbsProject *>(project())->isParsing() || m_job)
+ return false;
+
+ QbsBuildConfiguration *bc = static_cast<QbsBuildConfiguration *>(buildConfiguration());
+ if (!bc)
+ bc = static_cast<QbsBuildConfiguration *>(target()->activeBuildConfiguration());
+
+ if (!bc)
+ return false;
+
+ delete m_parser;
+ m_parser = new Internal::QbsParser;
+ ProjectExplorer::IOutputParser *parser = target()->kit()->createOutputParser();
+ if (parser)
+ m_parser->appendOutputParser(parser);
+
+ m_changedFiles = bc->changedFiles();
+
+ connect(m_parser, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)),
+ this, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)));
+ connect(m_parser, SIGNAL(addTask(ProjectExplorer::Task)),
+ this, SIGNAL(addTask(ProjectExplorer::Task)));
+
+ return true;
+}
+
+void QbsBuildStep::run(QFutureInterface<bool> &fi)
+{
+ m_fi = &fi;
+
+ QbsProject *pro = static_cast<QbsProject *>(project());
+ qbs::BuildOptions options(m_qbsBuildOptions);
+ options.changedFiles = m_changedFiles;
+
+ m_job = pro->build(options);
+
+ if (!m_job) {
+ m_fi->reportResult(false);
+ return;
+ }
+
+ m_progressBase = 0;
+
+ connect(m_job, SIGNAL(finished(bool,qbs::AbstractJob*)), this, SLOT(buildingDone(bool)));
+ connect(m_job, SIGNAL(taskStarted(QString,int,qbs::AbstractJob*)),
+ this, SLOT(handleTaskStarted(QString,int)));
+ connect(m_job, SIGNAL(taskProgress(int,qbs::AbstractJob*)),
+ this, SLOT(handleProgress(int)));
+ connect(m_job, SIGNAL(reportCommandDescription(QString,QString)),
+ this, SLOT(handleCommandDescriptionReport(QString,QString)));
+ connect(m_job, SIGNAL(reportWarning(qbs::Error)),
+ this, SLOT(handleWarningReport(qbs::Error)));
+ connect(m_job, SIGNAL(reportProcessResult(qbs::ProcessResult)),
+ this, SLOT(handleProcessResultReport(qbs::ProcessResult)));
+}
+
+ProjectExplorer::BuildStepConfigWidget *QbsBuildStep::createConfigWidget()
+{
+ return new QbsBuildStepConfigWidget(this);
+}
+
+bool QbsBuildStep::runInGuiThread() const
+{
+ return true;
+}
+
+void QbsBuildStep::cancel()
+{
+ if (m_job)
+ m_job->cancel();
+}
+
+QVariantMap QbsBuildStep::qbsConfiguration() const
+{
+ return m_qbsConfiguration;
+}
+
+void QbsBuildStep::setQbsConfiguration(const QVariantMap &config)
+{
+ QbsProject *pro = static_cast<QbsProject *>(project());
+
+ QVariantMap tmp = config;
+ tmp.insert(QLatin1String(Constants::QBS_CONFIG_PROFILE_KEY), pro->projectManager()->profileForKit(target()->kit()));
+ if (!tmp.contains(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)))
+ tmp.insert(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY),
+ QString::fromLatin1(Constants::QBS_VARIANT_DEBUG));
+
+ if (tmp == m_qbsConfiguration)
+ return;
+ m_qbsConfiguration = tmp;
+ emit qbsConfigurationChanged();
+}
+
+bool QbsBuildStep::dryRun() const
+{
+ return m_qbsBuildOptions.dryRun;
+}
+
+bool QbsBuildStep::keepGoing() const
+{
+ return m_qbsBuildOptions.keepGoing;
+}
+
+int QbsBuildStep::maxJobs() const
+{
+ return m_qbsBuildOptions.maxJobCount;
+}
+
+bool QbsBuildStep::fromMap(const QVariantMap &map)
+{
+ if (!ProjectExplorer::BuildStep::fromMap(map))
+ return false;
+
+ setQbsConfiguration(map.value(QLatin1String(QBS_CONFIG)).toMap());
+ m_qbsBuildOptions.dryRun = map.value(QLatin1String(QBS_DRY_RUN)).toBool();
+ m_qbsBuildOptions.keepGoing = map.value(QLatin1String(QBS_KEEP_GOING)).toBool();
+ m_qbsBuildOptions.maxJobCount = map.value(QLatin1String(QBS_MAXJOBCOUNT)).toInt();
+ return true;
+}
+
+QVariantMap QbsBuildStep::toMap() const
+{
+ QVariantMap map = ProjectExplorer::BuildStep::toMap();
+ map.insert(QLatin1String(QBS_CONFIG), m_qbsConfiguration);
+ map.insert(QLatin1String(QBS_DRY_RUN), m_qbsBuildOptions.dryRun);
+ map.insert(QLatin1String(QBS_KEEP_GOING), m_qbsBuildOptions.keepGoing);
+ map.insert(QLatin1String(QBS_MAXJOBCOUNT), m_qbsBuildOptions.maxJobCount);
+ return map;
+}
+
+void QbsBuildStep::buildingDone(bool success)
+{
+ // Report errors:
+ foreach (const qbs::ErrorData &data, m_job->error().entries())
+ createTaskAndOutput(ProjectExplorer::Task::Error, data.description(),
+ data.codeLocation().fileName, data.codeLocation().line);
+
+ QTC_ASSERT(m_fi, return);
+ m_fi->reportResult(success);
+ m_fi = 0; // do not delete, it is not ours
+ m_job->deleteLater();
+ m_job = 0;
+
+ emit finished();
+}
+
+void QbsBuildStep::handleTaskStarted(const QString &desciption, int max)
+{
+ Q_UNUSED(desciption);
+ QTC_ASSERT(m_fi, return);
+
+ m_progressBase = m_fi->progressValue();
+ m_fi->setProgressRange(0, m_progressBase + max);
+}
+
+void QbsBuildStep::handleProgress(int value)
+{
+ QTC_ASSERT(m_fi, return);
+ m_fi->setProgressValue(m_progressBase + value);
+}
+
+void QbsBuildStep::handleWarningReport(const qbs::Error &error)
+{
+ foreach (const qbs::ErrorData &data, error.entries()) {
+ createTaskAndOutput(ProjectExplorer::Task::Warning, data.description(),
+ data.codeLocation().fileName, data.codeLocation().line);
+ }
+}
+
+void QbsBuildStep::handleCommandDescriptionReport(const QString &highlight, const QString &message)
+{
+ Q_UNUSED(highlight);
+ emit addOutput(message, NormalOutput);
+}
+
+void QbsBuildStep::handleProcessResultReport(const qbs::ProcessResult &result)
+{
+ bool hasOutput = !result.stdOut.isEmpty() || !result.stdErr.isEmpty();
+
+ if (result.success && !hasOutput && !m_showCompilerOutput)
+ return;
+
+ m_parser->setWorkingDirectory(result.workingDirectory);
+ foreach (const QString &line, result.stdErr) {
+ m_parser->stdError(line);
+ addOutput(line, ErrorOutput);
+ }
+ foreach (const QString &line, result.stdOut) {
+ m_parser->stdOutput(line);
+ addOutput(line, NormalOutput);
+ }
+}
+
+void QbsBuildStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message,
+ const QString &file, int line)
+{
+ emit addTask(ProjectExplorer::Task(type, message,
+ Utils::FileName::fromString(file), line,
+ ProjectExplorer::Constants::TASK_CATEGORY_COMPILE));
+ emit addOutput(message, NormalOutput);
+}
+
+QString QbsBuildStep::buildVariant() const
+{
+ return qbsConfiguration().value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString();
+}
+
+void QbsBuildStep::setBuildVariant(const QString &variant)
+{
+ if (m_qbsConfiguration.value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString() == variant)
+ return;
+ m_qbsConfiguration.insert(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY), variant);
+ emit qbsConfigurationChanged();
+}
+
+QString QbsBuildStep::profile() const
+{
+ return qbsConfiguration().value(QLatin1String(Constants::QBS_CONFIG_PROFILE_KEY)).toString();
+}
+
+void QbsBuildStep::setDryRun(bool dr)
+{
+ if (m_qbsBuildOptions.dryRun == dr)
+ return;
+ m_qbsBuildOptions.dryRun = dr;
+ emit qbsBuildOptionsChanged();
+}
+
+void QbsBuildStep::setKeepGoing(bool kg)
+{
+ if (m_qbsBuildOptions.keepGoing == kg)
+ return;
+ m_qbsBuildOptions.keepGoing = kg;
+ emit qbsBuildOptionsChanged();
+}
+
+void QbsBuildStep::setMaxJobs(int jobcount)
+{
+ if (m_qbsBuildOptions.maxJobCount == jobcount)
+ return;
+ m_qbsBuildOptions.maxJobCount = jobcount;
+ emit qbsBuildOptionsChanged();
+}
+
+// --------------------------------------------------------------------
+// QbsBuildStepConfigWidget:
+// --------------------------------------------------------------------
+
+QbsBuildStepConfigWidget::QbsBuildStepConfigWidget(QbsBuildStep *step) :
+ m_step(step)
+{
+ connect(m_step, SIGNAL(displayNameChanged()), this, SLOT(updateState()));
+ connect(m_step, SIGNAL(qbsConfigurationChanged()), this, SLOT(updateState()));
+ connect(m_step, SIGNAL(qbsBuildOptionsChanged()), this, SLOT(updateState()));
+
+ setContentsMargins(0, 0, 0, 0);
+
+ m_ui = new Ui::QbsBuildStepConfigWidget;
+ m_ui->setupUi(this);
+
+ connect(m_ui->buildVariantComboBox, SIGNAL(currentIndexChanged(int)),
+ this, SLOT(changeBuildVariant(int)));
+ connect(m_ui->dryRunCheckBox, SIGNAL(toggled(bool)), this, SLOT(changeDryRun(bool)));
+ connect(m_ui->keepGoingCheckBox, SIGNAL(toggled(bool)), this, SLOT(changeKeepGoing(bool)));
+ connect(m_ui->jobSpinBox, SIGNAL(valueChanged(int)), this, SLOT(changeJobCount(int)));
+
+ updateState();
+}
+
+QString QbsBuildStepConfigWidget::summaryText() const
+{
+ return m_summary;
+}
+
+QString QbsBuildStepConfigWidget::displayName() const
+{
+ return m_step->displayName();
+}
+
+void QbsBuildStepConfigWidget::updateState()
+{
+ m_ui->dryRunCheckBox->setChecked(m_step->dryRun());
+ m_ui->keepGoingCheckBox->setChecked(m_step->keepGoing());
+ m_ui->jobSpinBox->setValue(m_step->maxJobs());
+
+ const QString buildVariant = m_step->buildVariant();
+ const int idx = (buildVariant == QLatin1String(Constants::QBS_VARIANT_DEBUG)) ? 0 : 1;
+ m_ui->buildVariantComboBox->setCurrentIndex(idx);
+
+ qbs::BuildOptions defaultOptions;
+
+ QString command = QLatin1String("qbs ");
+ if (m_step->dryRun())
+ command += QLatin1String("--dryRun ");
+ if (m_step->keepGoing())
+ command += QLatin1String("--keepGoing ");
+ if (m_step->maxJobs() != defaultOptions.maxJobCount)
+ command += QString::fromLatin1("--jobs %1 ").arg(m_step->maxJobs());
+ command += QString::fromLatin1("build profile:%1 %2").arg(m_step->profile(), buildVariant);
+
+ QString summary = tr("<b>Qbs:</b> %1").arg(command);
+ if (m_summary != summary) {
+ m_summary = summary;
+ emit updateSummary();
+ }
+}
+
+void QbsBuildStepConfigWidget::changeBuildVariant(int idx)
+{
+ QString variant;
+ if (idx == 1)
+ variant = QLatin1String(Constants::QBS_VARIANT_RELEASE);
+ else
+ variant = QLatin1String(Constants::QBS_VARIANT_DEBUG);
+ m_step->setBuildVariant(variant);
+}
+
+void QbsBuildStepConfigWidget::changeDryRun(bool dr)
+{
+ m_step->setDryRun(dr);
+}
+
+void QbsBuildStepConfigWidget::changeKeepGoing(bool kg)
+{
+ m_step->setKeepGoing(kg);
+}
+
+void QbsBuildStepConfigWidget::changeJobCount(int count)
+{
+ m_step->setMaxJobs(count);
+}
+
+// --------------------------------------------------------------------
+// QbsBuildStepFactory:
+// --------------------------------------------------------------------
+
+QbsBuildStepFactory::QbsBuildStepFactory(QObject *parent) :
+ ProjectExplorer::IBuildStepFactory(parent)
+{ }
+
+QList<Core::Id> QbsBuildStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const
+{
+ if (parent->id() == ProjectExplorer::Constants::BUILDSTEPS_BUILD
+ && qobject_cast<QbsBuildConfiguration *>(parent->parent()))
+ return QList<Core::Id>() << Core::Id(Constants::QBS_BUILDSTEP_ID);
+ return QList<Core::Id>();
+}
+
+QString QbsBuildStepFactory::displayNameForId(const Core::Id id) const
+{
+ if (id == Core::Id(Constants::QBS_BUILDSTEP_ID))
+ return tr("Qbs");
+ return QString();
+}
+
+bool QbsBuildStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const
+{
+ if (parent->id() != Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD)
+ || !qobject_cast<QbsBuildConfiguration *>(parent->parent()))
+ return false;
+ return id == Core::Id(Constants::QBS_BUILDSTEP_ID);
+}
+
+ProjectExplorer::BuildStep *QbsBuildStepFactory::create(ProjectExplorer::BuildStepList *parent, const Core::Id id)
+{
+ if (!canCreate(parent, id))
+ return 0;
+ return new QbsBuildStep(parent);
+}
+
+bool QbsBuildStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const
+{
+ return canCreate(parent, ProjectExplorer::idFromMap(map));
+}
+
+ProjectExplorer::BuildStep *QbsBuildStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map)
+{
+ if (!canRestore(parent, map))
+ return 0;
+ QbsBuildStep *bs = new QbsBuildStep(parent);
+ if (!bs->fromMap(map)) {
+ delete bs;
+ return 0;
+ }
+ return bs;
+}
+
+bool QbsBuildStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const
+{
+ return canCreate(parent, product->id());
+}
+
+ProjectExplorer::BuildStep *QbsBuildStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product)
+{
+ if (!canClone(parent, product))
+ return 0;
+ return new QbsBuildStep(parent, static_cast<QbsBuildStep *>(product));
+}
+
+} // namespace Internal
+} // namespace QbsProjectManager
diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.h b/src/plugins/qbsprojectmanager/qbsbuildstep.h
new file mode 100644
index 0000000000..9d5e1da394
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsbuildstep.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** 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 QBSBUILDSTEP_H
+#define QBSBUILDSTEP_H
+
+#include "qbsbuildconfiguration.h"
+
+#include <projectexplorer/buildstep.h>
+
+#include <qbs.h>
+
+namespace QbsProjectManager {
+namespace Internal {
+
+class QbsBuildStepConfigWidget;
+
+class QbsBuildStep : public ProjectExplorer::BuildStep
+{
+ Q_OBJECT
+
+public:
+ QbsBuildStep(ProjectExplorer::BuildStepList *bsl);
+ QbsBuildStep(ProjectExplorer::BuildStepList *bsl, const QbsBuildStep *other);
+ ~QbsBuildStep();
+
+ bool init();
+
+ void run(QFutureInterface<bool> &fi);
+
+ ProjectExplorer::BuildStepConfigWidget *createConfigWidget();
+
+ bool runInGuiThread() const;
+ void cancel();
+
+ QVariantMap qbsConfiguration() const;
+ void setQbsConfiguration(const QVariantMap &config);
+
+ bool dryRun() const;
+ bool keepGoing() const;
+ int maxJobs() const;
+ QString buildVariant() const;
+
+ bool fromMap(const QVariantMap &map);
+ QVariantMap toMap() const;
+
+signals:
+ void qbsConfigurationChanged();
+ void qbsBuildOptionsChanged();
+
+private slots:
+ void buildingDone(bool success);
+ void handleTaskStarted(const QString &desciption, int max);
+ void handleProgress(int value);
+ void handleWarningReport(const qbs::Error &error);
+ void handleCommandDescriptionReport(const QString &highlight, const QString &message);
+ void handleProcessResultReport(const qbs::ProcessResult &result);
+
+private:
+ void createTaskAndOutput(ProjectExplorer::Task::TaskType type,
+ const QString &message, const QString &file, int line);
+
+ void setBuildVariant(const QString &variant);
+ QString profile() const;
+
+ void setDryRun(bool dr);
+ void setKeepGoing(bool kg);
+ void setMaxJobs(int jobcount);
+
+ QVariantMap m_qbsConfiguration;
+ qbs::BuildOptions m_qbsBuildOptions;
+ QStringList m_changedFiles;
+
+ QFutureInterface<bool> *m_fi;
+ qbs::BuildJob *m_job;
+ int m_progressBase;
+ bool m_showCompilerOutput;
+ ProjectExplorer::IOutputParser *m_parser;
+
+ friend class QbsBuildStepConfigWidget;
+};
+
+namespace Ui { class QbsBuildStepConfigWidget; }
+
+class QbsBuildStepConfigWidget : public ProjectExplorer::BuildStepConfigWidget
+{
+ Q_OBJECT
+public:
+ QbsBuildStepConfigWidget(QbsBuildStep *step);
+ QString summaryText() const;
+ QString displayName() const;
+
+private slots:
+ void updateState();
+
+ void changeBuildVariant(int);
+ void changeDryRun(bool dr);
+ void changeKeepGoing(bool kg);
+ void changeJobCount(int count);
+
+private:
+ Ui::QbsBuildStepConfigWidget *m_ui;
+
+ QbsBuildStep *m_step;
+ QString m_summary;
+};
+
+
+class QbsBuildStepFactory : public ProjectExplorer::IBuildStepFactory
+{
+ Q_OBJECT
+
+public:
+ explicit QbsBuildStepFactory(QObject *parent = 0);
+
+ // used to show the list of possible additons to a target, returns a list of types
+ QList<Core::Id> availableCreationIds(ProjectExplorer::BuildStepList *parent) const;
+ // used to translate the types to names to display to the user
+ QString displayNameForId(const Core::Id id) const;
+
+ bool canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const;
+ ProjectExplorer::BuildStep *create(ProjectExplorer::BuildStepList *parent, const Core::Id id);
+ // used to recreate the runConfigurations when restoring settings
+ bool canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const;
+ ProjectExplorer::BuildStep *restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map);
+ bool canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const;
+ ProjectExplorer::BuildStep *clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product);
+};
+
+
+} // namespace Internal
+} // namespace QbsProjectManager
+
+#endif // QBSBUILDSTEP_H
diff --git a/src/plugins/qbsprojectmanager/qbsbuildstepconfigwidget.ui b/src/plugins/qbsprojectmanager/qbsbuildstepconfigwidget.ui
new file mode 100644
index 0000000000..cf0f48058c
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsbuildstepconfigwidget.ui
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QbsProjectManager::Internal::QbsBuildStepConfigWidget</class>
+ <widget class="QWidget" name="QbsProjectManager::Internal::QbsBuildStepConfigWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>440</width>
+ <height>97</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="verticalSpacing">
+ <number>16</number>
+ </property>
+ <item row="1" column="0" colspan="2">
+ <widget class="QFrame" name="frame">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="dryRunCheckBox">
+ <property name="text">
+ <string>Dry run</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="keepGoingCheckBox">
+ <property name="text">
+ <string>Keep Going</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QSpinBox" name="jobSpinBox"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>jobs</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="buildVariantComboBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Release</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="buildVariantLabel">
+ <property name="text">
+ <string>Build variant:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/qbsprojectmanager/qbscleanstep.cpp b/src/plugins/qbsprojectmanager/qbscleanstep.cpp
new file mode 100644
index 0000000000..e8cc6cd18f
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbscleanstep.cpp
@@ -0,0 +1,397 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "qbscleanstep.h"
+
+#include "qbsbuildconfiguration.h"
+#include "qbsproject.h"
+#include "qbsprojectmanagerconstants.h"
+
+#include "ui_qbscleanstepconfigwidget.h"
+
+#include <projectexplorer/buildsteplist.h>
+#include <projectexplorer/kit.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
+#include <utils/qtcassert.h>
+
+static const char QBS_CLEAN_ALL[] = "Qbs.CleanAll";
+static const char QBS_DRY_RUN[] = "Qbs.DryRun";
+static const char QBS_KEEP_GOING[] = "Qbs.DryKeepGoing";
+static const char QBS_MAXJOBCOUNT[] = "Qbs.MaxJobs";
+
+// --------------------------------------------------------------------
+// Constants:
+// --------------------------------------------------------------------
+
+namespace QbsProjectManager {
+namespace Internal {
+
+// --------------------------------------------------------------------
+// QbsCleanStep:
+// --------------------------------------------------------------------
+
+QbsCleanStep::QbsCleanStep(ProjectExplorer::BuildStepList *bsl) :
+ ProjectExplorer::BuildStep(bsl, Core::Id(Constants::QBS_CLEANSTEP_ID)),
+ m_cleanAll(false), m_job(0), m_showCompilerOutput(true), m_parser(0)
+{
+ setDisplayName(tr("qbs clean"));
+}
+
+QbsCleanStep::QbsCleanStep(ProjectExplorer::BuildStepList *bsl, const QbsCleanStep *other) :
+ ProjectExplorer::BuildStep(bsl, Core::Id(Constants::QBS_CLEANSTEP_ID)),
+ m_qbsBuildOptions(other->m_qbsBuildOptions), m_cleanAll(other->m_cleanAll), m_job(0),
+ m_showCompilerOutput(other->m_showCompilerOutput), m_parser(0)
+{ }
+
+QbsCleanStep::~QbsCleanStep()
+{
+ cancel();
+ m_job->deleteLater();
+ m_job = 0;
+}
+
+bool QbsCleanStep::init()
+{
+ if (static_cast<QbsProject *>(project())->isParsing() || m_job)
+ return false;
+
+ QbsBuildConfiguration *bc = static_cast<QbsBuildConfiguration *>(buildConfiguration());
+ if (!bc)
+ bc = static_cast<QbsBuildConfiguration *>(target()->activeBuildConfiguration());
+
+ if (!bc)
+ return false;
+
+ return true;
+}
+
+void QbsCleanStep::run(QFutureInterface<bool> &fi)
+{
+ m_fi = &fi;
+
+ QbsProject *pro = static_cast<QbsProject *>(project());
+ qbs::BuildOptions options(m_qbsBuildOptions);
+
+ m_job = pro->clean(options, m_cleanAll);
+
+ if (!m_job) {
+ m_fi->reportResult(false);
+ return;
+ }
+
+ m_progressBase = 0;
+
+ connect(m_job, SIGNAL(finished(bool,qbs::AbstractJob*)), this, SLOT(cleaningDone(bool)));
+ connect(m_job, SIGNAL(taskStarted(QString,int,qbs::AbstractJob*)),
+ this, SLOT(handleTaskStarted(QString,int)));
+ connect(m_job, SIGNAL(taskProgress(int,qbs::AbstractJob*)),
+ this, SLOT(handleProgress(int)));
+}
+
+ProjectExplorer::BuildStepConfigWidget *QbsCleanStep::createConfigWidget()
+{
+ return new QbsCleanStepConfigWidget(this);
+}
+
+bool QbsCleanStep::runInGuiThread() const
+{
+ return true;
+}
+
+void QbsCleanStep::cancel()
+{
+ if (m_job)
+ m_job->cancel();
+}
+
+bool QbsCleanStep::dryRun() const
+{
+ return m_qbsBuildOptions.dryRun;
+}
+
+bool QbsCleanStep::keepGoing() const
+{
+ return m_qbsBuildOptions.keepGoing;
+}
+
+int QbsCleanStep::maxJobs() const
+{
+ return m_qbsBuildOptions.maxJobCount;
+}
+
+bool QbsCleanStep::cleanAll() const
+{
+ return m_cleanAll;
+}
+
+bool QbsCleanStep::fromMap(const QVariantMap &map)
+{
+ if (!ProjectExplorer::BuildStep::fromMap(map))
+ return false;
+
+ m_qbsBuildOptions.dryRun = map.value(QLatin1String(QBS_DRY_RUN)).toBool();
+ m_qbsBuildOptions.keepGoing = map.value(QLatin1String(QBS_KEEP_GOING)).toBool();
+ m_qbsBuildOptions.maxJobCount = map.value(QLatin1String(QBS_MAXJOBCOUNT)).toInt();
+ m_cleanAll = map.value(QLatin1String(QBS_CLEAN_ALL)).toBool();
+
+ return true;
+}
+
+QVariantMap QbsCleanStep::toMap() const
+{
+ QVariantMap map = ProjectExplorer::BuildStep::toMap();
+ map.insert(QLatin1String(QBS_DRY_RUN), m_qbsBuildOptions.dryRun);
+ map.insert(QLatin1String(QBS_KEEP_GOING), m_qbsBuildOptions.keepGoing);
+ map.insert(QLatin1String(QBS_MAXJOBCOUNT), m_qbsBuildOptions.maxJobCount);
+ map.insert(QLatin1String(QBS_CLEAN_ALL), m_cleanAll);
+
+ return map;
+}
+
+void QbsCleanStep::cleaningDone(bool success)
+{
+ // Report errors:
+ foreach (const qbs::ErrorData &data, m_job->error().entries()) {
+ createTaskAndOutput(ProjectExplorer::Task::Error, data.description(),
+ data.codeLocation().fileName, data.codeLocation().line);
+ }
+
+ QTC_ASSERT(m_fi, return);
+ m_fi->reportResult(success);
+ m_fi = 0; // do not delete, it is not ours
+ m_job->deleteLater();
+ m_job = 0;
+
+ emit finished();
+}
+
+void QbsCleanStep::handleTaskStarted(const QString &desciption, int max)
+{
+ Q_UNUSED(desciption);
+ QTC_ASSERT(m_fi, return);
+ m_progressBase = m_fi->progressValue();
+ m_fi->setProgressRange(0, m_progressBase + max);
+}
+
+void QbsCleanStep::handleProgress(int value)
+{
+ QTC_ASSERT(m_fi, return);
+ m_fi->setProgressValue(m_progressBase + value);
+}
+
+void QbsCleanStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message, const QString &file, int line)
+{
+ emit addTask(ProjectExplorer::Task(type, message,
+ Utils::FileName::fromString(file), line,
+ ProjectExplorer::Constants::TASK_CATEGORY_COMPILE));
+ emit addOutput(message, NormalOutput);
+}
+
+void QbsCleanStep::setDryRun(bool dr)
+{
+ if (m_qbsBuildOptions.dryRun == dr)
+ return;
+ m_qbsBuildOptions.dryRun = dr;
+ emit changed();
+}
+
+void QbsCleanStep::setKeepGoing(bool kg)
+{
+ if (m_qbsBuildOptions.keepGoing == kg)
+ return;
+ m_qbsBuildOptions.keepGoing = kg;
+ emit changed();
+}
+
+void QbsCleanStep::setMaxJobs(int jobcount)
+{
+ if (m_qbsBuildOptions.maxJobCount == jobcount)
+ return;
+ m_qbsBuildOptions.maxJobCount = jobcount;
+ emit changed();
+}
+
+void QbsCleanStep::setCleanAll(bool ca)
+{
+ if (m_cleanAll == ca)
+ return;
+ m_cleanAll = ca;
+ emit changed();
+}
+
+// --------------------------------------------------------------------
+// QbsCleanStepConfigWidget:
+// --------------------------------------------------------------------
+
+QbsCleanStepConfigWidget::QbsCleanStepConfigWidget(QbsCleanStep *step) :
+ m_step(step)
+{
+ connect(m_step, SIGNAL(displayNameChanged()), this, SLOT(updateState()));
+ connect(m_step, SIGNAL(changed()), this, SLOT(updateState()));
+
+ setContentsMargins(0, 0, 0, 0);
+
+ m_ui = new Ui::QbsCleanStepConfigWidget;
+ m_ui->setupUi(this);
+
+ connect(m_ui->cleanAllCheckBox, SIGNAL(toggled(bool)),
+ this, SLOT(changeCleanAll(bool)));
+ connect(m_ui->dryRunCheckBox, SIGNAL(toggled(bool)), this, SLOT(changeDryRun(bool)));
+ connect(m_ui->keepGoingCheckBox, SIGNAL(toggled(bool)), this, SLOT(changeKeepGoing(bool)));
+ connect(m_ui->jobSpinBox, SIGNAL(valueChanged(int)), this, SLOT(changeJobCount(int)));
+
+ updateState();
+}
+
+QString QbsCleanStepConfigWidget::summaryText() const
+{
+ return m_summary;
+}
+
+QString QbsCleanStepConfigWidget::displayName() const
+{
+ return m_step->displayName();
+}
+
+void QbsCleanStepConfigWidget::updateState()
+{
+ m_ui->cleanAllCheckBox->setChecked(m_step->cleanAll());
+ m_ui->dryRunCheckBox->setChecked(m_step->dryRun());
+ m_ui->keepGoingCheckBox->setChecked(m_step->keepGoing());
+ m_ui->jobSpinBox->setValue(m_step->maxJobs());
+
+ qbs::BuildOptions defaultOptions;
+
+ QString command = QLatin1String("qbs clean ");
+ if (m_step->dryRun())
+ command += QLatin1String("--dryRun ");
+ if (m_step->keepGoing())
+ command += QLatin1String("--keepGoing ");
+ if (m_step->maxJobs() != defaultOptions.maxJobCount)
+ command += QString::fromLatin1("--jobs %1 ").arg(m_step->maxJobs());
+ if (m_step->cleanAll())
+ command += QLatin1String(" --all-artifacts");
+
+ QString summary = tr("<b>Qbs:</b> %1").arg(command);
+ if (m_summary != summary) {
+ m_summary = summary;
+ emit updateSummary();
+ }
+}
+
+void QbsCleanStepConfigWidget::changeCleanAll(bool ca)
+{
+ m_step->setCleanAll(ca);
+}
+
+void QbsCleanStepConfigWidget::changeDryRun(bool dr)
+{
+ m_step->setDryRun(dr);
+}
+
+void QbsCleanStepConfigWidget::changeKeepGoing(bool kg)
+{
+ m_step->setKeepGoing(kg);
+}
+
+void QbsCleanStepConfigWidget::changeJobCount(int count)
+{
+ m_step->setMaxJobs(count);
+}
+
+// --------------------------------------------------------------------
+// QbsCleanStepFactory:
+// --------------------------------------------------------------------
+
+QbsCleanStepFactory::QbsCleanStepFactory(QObject *parent) :
+ ProjectExplorer::IBuildStepFactory(parent)
+{ }
+
+QList<Core::Id> QbsCleanStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const
+{
+ if (parent->id() == ProjectExplorer::Constants::BUILDSTEPS_CLEAN
+ && qobject_cast<QbsBuildConfiguration *>(parent->parent()))
+ return QList<Core::Id>() << Core::Id(Constants::QBS_CLEANSTEP_ID);
+ return QList<Core::Id>();
+}
+
+QString QbsCleanStepFactory::displayNameForId(const Core::Id id) const
+{
+ if (id == Core::Id(Constants::QBS_CLEANSTEP_ID))
+ return tr("Qbs");
+ return QString();
+}
+
+bool QbsCleanStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const
+{
+ if (parent->id() != Core::Id(ProjectExplorer::Constants::BUILDSTEPS_CLEAN)
+ || !qobject_cast<QbsBuildConfiguration *>(parent->parent()))
+ return false;
+ return id == Core::Id(Constants::QBS_CLEANSTEP_ID);
+}
+
+ProjectExplorer::BuildStep *QbsCleanStepFactory::create(ProjectExplorer::BuildStepList *parent, const Core::Id id)
+{
+ if (!canCreate(parent, id))
+ return 0;
+ return new QbsCleanStep(parent);
+}
+
+bool QbsCleanStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const
+{
+ return canCreate(parent, ProjectExplorer::idFromMap(map));
+}
+
+ProjectExplorer::BuildStep *QbsCleanStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map)
+{
+ if (!canRestore(parent, map))
+ return 0;
+ QbsCleanStep *bs = new QbsCleanStep(parent);
+ if (!bs->fromMap(map)) {
+ delete bs;
+ return 0;
+ }
+ return bs;
+}
+
+bool QbsCleanStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const
+{
+ return canCreate(parent, product->id());
+}
+
+ProjectExplorer::BuildStep *QbsCleanStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product)
+{
+ if (!canClone(parent, product))
+ return 0;
+ return new QbsCleanStep(parent, static_cast<QbsCleanStep *>(product));
+}
+
+} // namespace Internal
+} // namespace QbsProjectManager
diff --git a/src/plugins/qbsprojectmanager/qbscleanstep.h b/src/plugins/qbsprojectmanager/qbscleanstep.h
new file mode 100644
index 0000000000..427e069260
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbscleanstep.h
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** 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 QBSCLEANSTEP_H
+#define QBSCLEANSTEP_H
+
+#include "qbsbuildconfiguration.h"
+
+#include <projectexplorer/buildstep.h>
+
+#include <qbs.h>
+
+namespace QbsProjectManager {
+namespace Internal {
+
+class QbsCleanStepConfigWidget;
+
+class QbsCleanStep : public ProjectExplorer::BuildStep
+{
+ Q_OBJECT
+
+public:
+ QbsCleanStep(ProjectExplorer::BuildStepList *bsl);
+ QbsCleanStep(ProjectExplorer::BuildStepList *bsl, const QbsCleanStep *other);
+ ~QbsCleanStep();
+
+ bool init();
+
+ void run(QFutureInterface<bool> &fi);
+
+ ProjectExplorer::BuildStepConfigWidget *createConfigWidget();
+
+ bool runInGuiThread() const;
+ void cancel();
+
+ bool fromMap(const QVariantMap &map);
+ QVariantMap toMap() const;
+
+ bool dryRun() const;
+ bool keepGoing() const;
+ int maxJobs() const;
+ bool cleanAll() const;
+
+signals:
+ void changed();
+
+private slots:
+ void cleaningDone(bool success);
+ void handleTaskStarted(const QString &desciption, int max);
+ void handleProgress(int value);
+
+private:
+ void createTaskAndOutput(ProjectExplorer::Task::TaskType type,
+ const QString &message, const QString &file, int line);
+
+ void setDryRun(bool dr);
+ void setKeepGoing(bool kg);
+ void setMaxJobs(int jobcount);
+ void setCleanAll(bool ca);
+
+ qbs::BuildOptions m_qbsBuildOptions;
+ bool m_cleanAll;
+
+ QFutureInterface<bool> *m_fi;
+ qbs::CleanJob *m_job;
+ int m_progressBase;
+ bool m_showCompilerOutput;
+ ProjectExplorer::IOutputParser *m_parser;
+
+ friend class QbsCleanStepConfigWidget;
+};
+
+namespace Ui { class QbsCleanStepConfigWidget; }
+
+class QbsCleanStepConfigWidget : public ProjectExplorer::BuildStepConfigWidget
+{
+ Q_OBJECT
+public:
+ QbsCleanStepConfigWidget(QbsCleanStep *step);
+ QString summaryText() const;
+ QString displayName() const;
+
+private slots:
+ void updateState();
+
+ void changeCleanAll(bool ca);
+ void changeDryRun(bool dr);
+ void changeKeepGoing(bool kg);
+ void changeJobCount(int jobcount);
+
+private:
+ Ui::QbsCleanStepConfigWidget *m_ui;
+
+ QbsCleanStep *m_step;
+ QString m_summary;
+};
+
+
+class QbsCleanStepFactory : public ProjectExplorer::IBuildStepFactory
+{
+ Q_OBJECT
+
+public:
+ explicit QbsCleanStepFactory(QObject *parent = 0);
+
+ // used to show the list of possible additons to a target, returns a list of types
+ QList<Core::Id> availableCreationIds(ProjectExplorer::BuildStepList *parent) const;
+ // used to translate the types to names to display to the user
+ QString displayNameForId(const Core::Id id) const;
+
+ bool canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const;
+ ProjectExplorer::BuildStep *create(ProjectExplorer::BuildStepList *parent, const Core::Id id);
+ // used to recreate the runConfigurations when restoring settings
+ bool canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const;
+ ProjectExplorer::BuildStep *restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map);
+ bool canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const;
+ ProjectExplorer::BuildStep *clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product);
+};
+
+
+} // namespace Internal
+} // namespace QbsProjectManager
+
+#endif // QBSCLEANSTEP_H
diff --git a/src/plugins/qbsprojectmanager/qbscleanstepconfigwidget.ui b/src/plugins/qbsprojectmanager/qbscleanstepconfigwidget.ui
new file mode 100644
index 0000000000..355f9b883d
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbscleanstepconfigwidget.ui
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QbsProjectManager::Internal::QbsCleanStepConfigWidget</class>
+ <widget class="QWidget" name="QbsProjectManager::Internal::QbsCleanStepConfigWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>440</width>
+ <height>59</height>
+ </rect>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QCheckBox" name="cleanAllCheckBox">
+ <property name="text">
+ <string>Clean all artifacts</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="frame">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="dryRunCheckBox">
+ <property name="text">
+ <string>Dry run</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="keepGoingCheckBox">
+ <property name="text">
+ <string>Keep Going</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QSpinBox" name="jobSpinBox"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>jobs</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/qbsprojectmanager/qbslogsink.cpp b/src/plugins/qbsprojectmanager/qbslogsink.cpp
new file mode 100644
index 0000000000..bbfc449d2f
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbslogsink.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "qbslogsink.h"
+
+#include <qbs.h>
+
+#include <coreplugin/messagemanager.h>
+
+#include <QCoreApplication>
+#include <QMutexLocker>
+#include <QTimer>
+
+namespace QbsProjectManager {
+namespace Internal {
+
+// --------------------------------------------------------------------
+// QbsLogSink:
+// --------------------------------------------------------------------
+
+void QbsLogSink::sendMessages()
+{
+ QStringList toSend;
+ {
+ QMutexLocker l(&m_mutex);
+ toSend = m_messages;
+ m_messages.clear();
+ }
+
+ Core::MessageManager *mm = Core::MessageManager::instance();
+ foreach (const QString &msg, toSend)
+ mm->printToOutputPanePopup(msg);
+}
+
+void QbsLogSink::outputLogMessage(qbs::LoggerLevel level, const qbs::LogMessage &logMessage)
+{
+ QString msg;
+ if (logMessage.printLogLevel) {
+ QByteArray levelTag = qbs::Logger::logLevelTag(level);
+ qbs::Internal::TextColor color = qbs::Internal::TextColorDefault;
+ switch (level) {
+ case qbs::LoggerError:
+ color = qbs::Internal::TextColorRed;
+ break;
+ case qbs::LoggerWarning:
+ color = qbs::Internal::TextColorYellow;
+ break;
+ default:
+ break;
+ }
+ msg = colorize(color, levelTag);
+ }
+
+ msg.append(colorize(logMessage.textColor, logMessage.data));
+
+ {
+ QMutexLocker l(&m_mutex);
+ m_messages.append(msg);
+ }
+ QMetaObject::invokeMethod(this, "sendMessages", Qt::QueuedConnection);
+}
+
+QString QbsLogSink::colorize(qbs::Internal::TextColor color, const QByteArray &text)
+{
+ switch (color) {
+ case qbs::Internal::TextColorBlack:
+ return QString::fromLatin1("<font color=\"rgb(0,0, 0)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorDarkRed:
+ return QString::fromLatin1("<font color=\"rgb(170,0,0)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorDarkGreen:
+ return QString::fromLatin1("<font color=\"rgb(0,170,0)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorDarkBlue:
+ return QString::fromLatin1("<font color=\"rgb(0,0,170)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorDarkCyan:
+ return QString::fromLatin1("<font color=\"rgb(0,170,170)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorDarkMagenta:
+ return QString::fromLatin1("<font color=\"rgb(170,0,170)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorDarkYellow:
+ return QString::fromLatin1("<font color=\"rgb(170,85,0)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorGray:
+ return QString::fromLatin1("<font color=\"rgb(170,170,170)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorRed:
+ return QString::fromLatin1("<font color=\"rgb(255,85,85)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorGreen:
+ return QString::fromLatin1("<font color=\"rgb(85,255,85)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorBlue:
+ return QString::fromLatin1("<font color=\"rgb(85,85,255)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorCyan:
+ return QString::fromLatin1("<font color=\"rgb(85,255,255)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorMagenta:
+ return QString::fromLatin1("<font color=\"rgb(255,85,255)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorYellow:
+ return QString::fromLatin1("<font color=\"rgb(255,255,85)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorWhite:
+ return QString::fromLatin1("<font color=\"rgb(255,255,255)\">%1</font>").arg(QString::fromLocal8Bit(text));
+ case qbs::Internal::TextColorBright:
+ case qbs::Internal::TextColorDefault:
+ // fallthrough:
+ default:
+ return QString::fromLocal8Bit(text);
+ }
+}
+
+} // namespace Internal
+} // namespace QbsProjectManager
diff --git a/src/plugins/qbsprojectmanager/qbslogsink.h b/src/plugins/qbsprojectmanager/qbslogsink.h
new file mode 100644
index 0000000000..78e0836d39
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbslogsink.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** 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 QBSLOGSINK_H
+#define QBSLOGSINK_H
+
+#include <qbs.h>
+
+#include <QMutex>
+#include <QObject>
+#include <QStringList>
+
+namespace QbsProjectManager {
+namespace Internal {
+
+class QbsLogSink : public QObject, public qbs::ILogSink
+{
+ Q_OBJECT
+
+private slots:
+ void sendMessages();
+
+private:
+ void outputLogMessage(qbs::LoggerLevel level, const qbs::LogMessage &logMessage);
+
+ QString colorize(qbs::TextColor color, const QByteArray &text);
+
+ QStringList m_messages;
+ QMutex m_mutex;
+};
+
+} // namespace Internal
+} // namespace QbsProjectManager
+
+#endif // QBSLOGSINK_H
diff --git a/src/plugins/qbsprojectmanager/qbsnodes.cpp b/src/plugins/qbsprojectmanager/qbsnodes.cpp
new file mode 100644
index 0000000000..0d89764041
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsnodes.cpp
@@ -0,0 +1,462 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "qbsnodes.h"
+
+#include "qbsproject.h"
+
+#include <utils/qtcassert.h>
+
+#include <qbs.h>
+
+#include <QDir>
+
+// <debug>
+#include <QDebug>
+// </debug>
+
+// ----------------------------------------------------------------------
+// Helpers:
+// ----------------------------------------------------------------------
+
+namespace QbsProjectManager {
+namespace Internal {
+
+class FileTreeNode {
+public:
+ FileTreeNode(const QString &n, FileTreeNode *p = 0) :
+ parent(p), name(n)
+ {
+ // <debug>
+ Q_ASSERT((!name.isEmpty() && p) || (name.isEmpty() && !p));
+ // </debug>
+ if (p)
+ p->children.append(this);
+ }
+
+ ~FileTreeNode()
+ {
+ qDeleteAll(children);
+ }
+
+ FileTreeNode *addPart(const QString &n)
+ {
+ foreach (FileTreeNode *c, children) {
+ if (c->name == n)
+ return c;
+ }
+ return new FileTreeNode(n, this);
+ }
+
+ bool isFile() { return children.isEmpty(); }
+
+ static void simplify(FileTreeNode *node)
+ {
+ foreach (FileTreeNode *c, node->children)
+ simplify(c);
+
+ if (node->children.count() == 1) {
+ FileTreeNode *child = node->children.at(0);
+ if (child->isFile())
+ return;
+
+ node->name = node->name + QLatin1Char('/') + child->name;
+ node->children = child->children;
+
+ foreach (FileTreeNode *tmpChild, node->children)
+ tmpChild->parent = node;
+
+ child->children.clear();
+ child->parent = 0;
+ delete child;
+
+ // <debug>
+ Q_ASSERT(!node->name.isEmpty());
+ // </debug>
+ return;
+ }
+ }
+
+ QString path()
+ {
+ QString p = name;
+ FileTreeNode *node = parent;
+ while (node) {
+ p = node->name + QLatin1Char('/') + p;
+ node = node->parent;
+ }
+ return p;
+ }
+
+ QList<FileTreeNode *> children;
+ FileTreeNode *parent;
+ QString name;
+};
+
+// ----------------------------------------------------------------------
+// QbsFileNode:
+// ----------------------------------------------------------------------
+
+QbsFileNode::QbsFileNode(const QString &filePath, const ProjectExplorer::FileType fileType,
+ bool generated, int line) :
+ ProjectExplorer::FileNode(filePath, fileType, generated),
+ m_line(line)
+{ }
+
+void QbsFileNode::setLine(int l)
+{
+ m_line = l;
+}
+
+// ---------------------------------------------------------------------------
+// QbsBaseProjectNode:
+// ---------------------------------------------------------------------------
+
+QbsBaseProjectNode::QbsBaseProjectNode(const QString &path) :
+ ProjectExplorer::ProjectNode(path)
+{ }
+
+bool QbsBaseProjectNode::hasBuildTargets() const
+{
+ foreach (ProjectNode *n, subProjectNodes())
+ if (n->hasBuildTargets())
+ return true;
+ return false;
+}
+
+QList<ProjectExplorer::ProjectNode::ProjectAction> QbsBaseProjectNode::supportedActions(ProjectExplorer::Node *node) const
+{
+ Q_UNUSED(node);
+ return QList<ProjectExplorer::ProjectNode::ProjectAction>();
+}
+
+bool QbsBaseProjectNode::canAddSubProject(const QString &proFilePath) const
+{
+ Q_UNUSED(proFilePath);
+ return false;
+}
+
+bool QbsBaseProjectNode::addSubProjects(const QStringList &proFilePaths)
+{
+ Q_UNUSED(proFilePaths);
+ return false;
+}
+
+bool QbsBaseProjectNode::removeSubProjects(const QStringList &proFilePaths)
+{
+ Q_UNUSED(proFilePaths);
+ return false;
+}
+
+bool QbsBaseProjectNode::addFiles(const ProjectExplorer::FileType fileType, const QStringList &filePaths, QStringList *notAdded)
+{
+ Q_UNUSED(fileType);
+ Q_UNUSED(filePaths);
+ Q_UNUSED(notAdded);
+ return false;
+}
+
+bool QbsBaseProjectNode::removeFiles(const ProjectExplorer::FileType fileType, const QStringList &filePaths, QStringList *notRemoved)
+{
+ Q_UNUSED(fileType);
+ Q_UNUSED(filePaths);
+ Q_UNUSED(notRemoved);
+ return false;
+}
+
+bool QbsBaseProjectNode::deleteFiles(const ProjectExplorer::FileType fileType, const QStringList &filePaths)
+{
+ Q_UNUSED(fileType);
+ Q_UNUSED(filePaths);
+ return false;
+}
+
+bool QbsBaseProjectNode::renameFile(const ProjectExplorer::FileType fileType, const QString &filePath, const QString &newFilePath)
+{
+ Q_UNUSED(fileType);
+ Q_UNUSED(filePath);
+ Q_UNUSED(newFilePath);
+ return false;
+}
+
+QList<ProjectExplorer::RunConfiguration *> QbsBaseProjectNode::runConfigurationsFor(ProjectExplorer::Node *node)
+{
+ Q_UNUSED(node);
+ return QList<ProjectExplorer::RunConfiguration *>();
+}
+
+// --------------------------------------------------------------------
+// QbsGroupNode:
+// --------------------------------------------------------------------
+
+QbsGroupNode::QbsGroupNode(const qbs::GroupData *grp) :
+ QbsBaseProjectNode(QString()),
+ m_group(0)
+{
+ setGroup(grp);
+}
+
+bool QbsGroupNode::isEnabled() const
+{
+ return static_cast<QbsProductNode *>(parentFolderNode())->isEnabled() && group()->isEnabled();
+}
+
+void QbsGroupNode::setGroup(const qbs::GroupData *group)
+{
+ if (group == m_group)
+ return;
+
+ setPath(group->location().fileName);
+ setDisplayName(group->name());
+
+ // Set Product file node used to jump to the product
+ QbsFileNode *indexFile = 0;
+ if (!m_group) {
+ indexFile = new QbsFileNode(group->location().fileName,
+ ProjectExplorer::ProjectFileType, false,
+ group->location().line);
+ addFileNodes(QList<ProjectExplorer::FileNode *>() << indexFile, this);
+ } else {
+ indexFile = static_cast<QbsFileNode *>(fileNodes().first());
+ indexFile->setPath(group->location().fileName);
+ indexFile->setLine(group->location().line);
+ indexFile->emitNodeUpdated();
+ }
+
+ // Build up a tree of nodes:
+ FileTreeNode *tree = new FileTreeNode(QString());
+
+ foreach (const QString &path, group->allFilePaths()) {
+ QStringList pathSegments = path.split(QLatin1Char('/'), QString::SkipEmptyParts);
+
+ FileTreeNode *root = tree;
+ while (!pathSegments.isEmpty())
+ root = root->addPart(pathSegments.takeFirst());
+ }
+
+ FileTreeNode::simplify(tree);
+ setupFolders(this, tree, indexFile);
+
+ delete tree;
+
+ m_group = group;
+ emitNodeUpdated();
+}
+
+void QbsGroupNode::setupFolders(ProjectExplorer::FolderNode *root, FileTreeNode *node,
+ ProjectExplorer::FileNode *keep)
+{
+ // insert initial folder:
+ if (!node->parent && !node->name.isEmpty()) {
+ ProjectExplorer::FolderNode *fn = root->findSubFolder(node->name);
+ if (!fn) {
+ fn = new ProjectExplorer::FolderNode(node->name);
+ addFolderNodes(QList<ProjectExplorer::FolderNode *>() << fn, root);
+ }
+ root = fn;
+ }
+
+ QList<ProjectExplorer::FileNode *> filesToRemove = root->fileNodes();
+ filesToRemove.removeAll(keep);
+ QList<ProjectExplorer::FileNode *> filesToAdd;
+
+ QList<ProjectExplorer::FolderNode *> foldersToRemove = root->subFolderNodes();
+
+ // insert subfolders
+ foreach (FileTreeNode *c, node->children) {
+ QString path = c->path();
+ if (c->isFile()) {
+ ProjectExplorer::FileNode *fn = root->findFile(path);
+ if (fn) {
+ fn->emitNodeUpdated(); // enabled might have changed
+ filesToRemove.removeOne(fn);
+ } else {
+ fn = new ProjectExplorer::FileNode(path, ProjectExplorer::UnknownFileType, false);
+ filesToAdd.append(fn);
+ }
+ } else {
+ ProjectExplorer::FolderNode *fn = root->findSubFolder(path);
+ path = path.mid(root->path().length() + 1); // remove common prefix
+ if (fn) {
+ fn->emitNodeUpdated(); // enabled might have changed
+ foldersToRemove.removeOne(fn);
+ } else {
+ fn = new ProjectExplorer::FolderNode(path);
+ addFolderNodes(QList<ProjectExplorer::FolderNode *>() << fn, root);
+ }
+ setupFolders(fn, c);
+ }
+ }
+ addFileNodes(filesToAdd, root);
+ removeFileNodes(filesToRemove, root);
+ removeFolderNodes(foldersToRemove, root);
+}
+
+// --------------------------------------------------------------------
+// QbsProductNode:
+// --------------------------------------------------------------------
+
+QbsProductNode::QbsProductNode(const qbs::ProductData *prd) :
+ QbsBaseProjectNode(prd->location().fileName),
+ m_product(0)
+{
+ setProduct(prd);
+}
+
+bool QbsProductNode::isEnabled() const
+{
+ return product()->isEnabled();
+}
+
+void QbsProductNode::setProduct(const qbs::ProductData *prd)
+{
+ if (m_product == prd)
+ return;
+
+ setDisplayName(prd->name());
+ setPath(prd->location().fileName);
+
+ // Set Product file node used to jump to the product
+ QList<ProjectExplorer::FileNode *> files = fileNodes();
+ if (files.isEmpty()) {
+ addFileNodes(QList<ProjectExplorer::FileNode *>()
+ << new QbsFileNode(prd->location().fileName,
+ ProjectExplorer::ProjectFileType, false,
+ prd->location().line),
+ this);
+ } else {
+ QbsFileNode *qbsFile = static_cast<QbsFileNode *>(files.at(0));
+ qbsFile->setPath(prd->location().fileName);
+ qbsFile->setLine(prd->location().line);
+ }
+
+ QList<ProjectExplorer::ProjectNode *> toAdd;
+ QList<ProjectExplorer::ProjectNode *> toRemove = subProjectNodes();
+
+ foreach (const qbs::GroupData &grp, prd->groups()) {
+ QbsGroupNode *qn = findGroupNode(grp.name());
+ if (qn) {
+ toRemove.removeAll(qn);
+ qn->setGroup(&grp);
+ } else {
+ toAdd << new QbsGroupNode(&grp);
+ }
+ }
+
+ addProjectNodes(toAdd);
+ removeProjectNodes(toRemove);
+
+ m_product = prd;
+ emitNodeUpdated();
+}
+
+QbsGroupNode *QbsProductNode::findGroupNode(const QString &name)
+{
+ foreach (ProjectExplorer::ProjectNode *n, subProjectNodes()) {
+ QbsGroupNode *qn = static_cast<QbsGroupNode *>(n);
+ if (qn->group()->name() == name)
+ return qn;
+ }
+ return 0;
+}
+
+// --------------------------------------------------------------------
+// QbsProjectNode:
+// --------------------------------------------------------------------
+
+QbsProjectNode::QbsProjectNode(const QString &projectFile) :
+ QbsBaseProjectNode(projectFile),
+ m_project(0), m_projectData(0)
+{
+ addFileNodes(QList<ProjectExplorer::FileNode *>()
+ << new ProjectExplorer::FileNode(projectFile, ProjectExplorer::ProjectFileType, false), this);
+}
+
+QbsProjectNode::~QbsProjectNode()
+{
+ delete m_projectData;
+ delete m_project;
+}
+
+void QbsProjectNode::update(const qbs::Project *prj)
+{
+ QList<ProjectExplorer::ProjectNode *> toAdd;
+ QList<ProjectExplorer::ProjectNode *> toRemove = subProjectNodes();
+
+ qbs::ProjectData *newData = 0;
+
+ if (prj) {
+ newData = new qbs::ProjectData(prj->projectData());
+ foreach (const qbs::ProductData &prd, newData->products()) {
+ QbsProductNode *qn = findProductNode(prd.name());
+ if (!qn) {
+ toAdd << new QbsProductNode(&prd);
+ } else {
+ qn->setProduct(&prd);
+ toRemove.removeOne(qn);
+ }
+ }
+ }
+
+ delete m_projectData;
+ m_projectData = newData;
+
+ if (m_project) {
+ delete m_project;
+ m_project = 0;
+ }
+
+ m_project = prj;
+
+ removeProjectNodes(toRemove);
+ addProjectNodes(toAdd);
+}
+
+const qbs::Project *QbsProjectNode::project() const
+{
+ return m_project;
+}
+
+const qbs::ProjectData *QbsProjectNode::projectData() const
+{
+ return m_projectData;
+}
+
+QbsProductNode *QbsProjectNode::findProductNode(const QString &name)
+{
+ foreach (ProjectExplorer::ProjectNode *n, subProjectNodes()) {
+ QbsProductNode *qn = static_cast<QbsProductNode *>(n);
+ if (qn->product()->name() == name)
+ return qn;
+ }
+ return 0;
+}
+
+} // namespace Internal
+} // namespace QbsProjectManager
diff --git a/src/plugins/qbsprojectmanager/qbsnodes.h b/src/plugins/qbsprojectmanager/qbsnodes.h
new file mode 100644
index 0000000000..c398d4b722
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsnodes.h
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** 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 QBSNODES_H
+#define QBSNODES_H
+
+#include <projectexplorer/projectnodes.h>
+
+#include <api/projectdata.h>
+
+namespace qbs { class Project; }
+
+namespace QbsProjectManager {
+namespace Internal {
+
+class FileTreeNode;
+class QbsProjectFile;
+
+// ----------------------------------------------------------------------
+// QbsFileNode:
+// ----------------------------------------------------------------------
+
+class QbsFileNode : public ProjectExplorer::FileNode
+{
+ Q_OBJECT
+public:
+ QbsFileNode(const QString &filePath, const ProjectExplorer::FileType fileType, bool generated,
+ int line);
+ int line() const { return m_line; }
+
+ void setLine(int l);
+
+private:
+ int m_line;
+};
+
+// ---------------------------------------------------------------------------
+// QbsBaseProjectNode:
+// ---------------------------------------------------------------------------
+
+class QbsBaseProjectNode : public ProjectExplorer::ProjectNode
+{
+ Q_OBJECT
+
+public:
+ explicit QbsBaseProjectNode(const QString &path);
+
+ bool hasBuildTargets() const;
+
+ QList<ProjectAction> supportedActions(Node *node) const;
+
+ bool canAddSubProject(const QString &proFilePath) const;
+
+ bool addSubProjects(const QStringList &proFilePaths);
+
+ bool removeSubProjects(const QStringList &proFilePaths);
+
+ bool addFiles(const ProjectExplorer::FileType fileType,
+ const QStringList &filePaths,
+ QStringList *notAdded = 0);
+ bool removeFiles(const ProjectExplorer::FileType fileType,
+ const QStringList &filePaths,
+ QStringList *notRemoved = 0);
+ bool deleteFiles(const ProjectExplorer::FileType fileType,
+ const QStringList &filePaths);
+ bool renameFile(const ProjectExplorer::FileType fileType,
+ const QString &filePath,
+ const QString &newFilePath);
+
+ QList<ProjectExplorer::RunConfiguration *> runConfigurationsFor(Node *node);
+};
+
+// --------------------------------------------------------------------
+// QbsGroupNode:
+// --------------------------------------------------------------------
+
+class QbsGroupNode : public QbsBaseProjectNode
+{
+ Q_OBJECT
+
+public:
+ QbsGroupNode(const qbs::GroupData *grp);
+
+ bool isEnabled() const;
+ void setGroup(const qbs::GroupData *group);
+ const qbs::GroupData *group() const { return m_group; }
+
+private:
+ void setupFolders(ProjectExplorer::FolderNode *root, FileTreeNode *node,
+ ProjectExplorer::FileNode *keep = 0);
+
+ const qbs::GroupData *m_group;
+};
+
+// --------------------------------------------------------------------
+// QbsProductNode:
+// --------------------------------------------------------------------
+
+class QbsProductNode : public QbsBaseProjectNode
+{
+ Q_OBJECT
+
+public:
+ explicit QbsProductNode(const qbs::ProductData *prd);
+
+ bool isEnabled() const;
+
+ void setProduct(const qbs::ProductData *prd);
+ const qbs::ProductData *product() const { return m_product; }
+
+private:
+ QbsGroupNode *findGroupNode(const QString &name);
+
+ const qbs::ProductData *m_product;
+};
+
+// ---------------------------------------------------------------------------
+// QbsProjectNode:
+// ---------------------------------------------------------------------------
+
+class QbsProjectNode : public QbsBaseProjectNode
+{
+ Q_OBJECT
+
+public:
+ explicit QbsProjectNode(const QString &projectFile);
+ ~QbsProjectNode();
+
+ void update(const qbs::Project *prj);
+
+ const qbs::Project *project() const;
+ const qbs::ProjectData *projectData() const;
+
+private:
+ QbsProductNode *findProductNode(const QString &name);
+
+ const qbs::Project *m_project;
+ const qbs::ProjectData *m_projectData;
+};
+} // namespace Internal
+} // namespace QbsProjectManager
+
+#endif // QBSNODES_H
diff --git a/src/plugins/qbsprojectmanager/qbsparser.cpp b/src/plugins/qbsprojectmanager/qbsparser.cpp
new file mode 100644
index 0000000000..d1d2a341c2
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsparser.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "qbsparser.h"
+
+#include <projectexplorer/task.h>
+
+#include <utils/fileutils.h>
+
+#include <QFileInfo>
+
+namespace QbsProjectManager {
+namespace Internal {
+
+QbsParser::QbsParser()
+{
+ setObjectName(QLatin1String("QbsParser"));
+}
+
+void QbsParser::setWorkingDirectory(const QString &workingDirectory)
+{
+ m_workingDirectory = QDir(workingDirectory);
+ IOutputParser::setWorkingDirectory(workingDirectory);
+}
+
+void QbsParser::taskAdded(const ProjectExplorer::Task &task)
+{
+ ProjectExplorer::Task editable(task);
+
+ QString filePath = task.file.toString();
+
+ if (!filePath.isEmpty())
+ editable.file = Utils::FileName::fromUserInput(m_workingDirectory.absoluteFilePath(filePath));
+
+ IOutputParser::taskAdded(editable);
+}
+
+} // namespace Internal
+} // namespace QbsProjectManager
+
diff --git a/src/plugins/qbsprojectmanager/qbsparser.h b/src/plugins/qbsprojectmanager/qbsparser.h
new file mode 100644
index 0000000000..8133f25af5
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsparser.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** 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 QBSPARSER_H
+#define QBSPARSER_H
+
+#include "qbsprojectmanager_global.h"
+
+#include <projectexplorer/ioutputparser.h>
+
+#include <QDir>
+
+namespace QbsProjectManager {
+namespace Internal {
+
+class QBSPROJECTMANAGER_EXPORT QbsParser : public ProjectExplorer::IOutputParser
+{
+ Q_OBJECT
+
+public:
+ explicit QbsParser();
+
+ void setWorkingDirectory(const QString &workingDirectory);
+
+public slots:
+ void taskAdded(const ProjectExplorer::Task &task);
+
+private:
+ QDir m_workingDirectory;
+};
+
+} // namespace Internal
+} // namespace ProjectExplorer
+
+#endif // QBSPARSER_H
diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp
new file mode 100644
index 0000000000..588ee3a60c
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsproject.cpp
@@ -0,0 +1,655 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "qbsproject.h"
+
+#include "qbsbuildconfiguration.h"
+#include "qbsprojectfile.h"
+#include "qbsprojectmanagerconstants.h"
+#include "qbsnodes.h"
+
+#include <coreplugin/documentmanager.h>
+#include <utils/qtcassert.h>
+
+#include <language/language.h>
+
+#include <coreplugin/icontext.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/id.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+#include <coreplugin/mimedatabase.h>
+#include <cpptools/cppmodelmanager.h>
+#include <projectexplorer/buildenvironmentwidget.h>
+#include <projectexplorer/kit.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
+#include <projectexplorer/taskhub.h>
+#include <qtsupport/qtkitinformation.h>
+
+#include <qmljs/qmljsmodelmanagerinterface.h>
+
+#include <qbs.h>
+#include <tools/scripttools.h> // qbs, remove once there is a expand method in Qbs itself!
+
+#include <QFileInfo>
+
+// --------------------------------------------------------------------
+// Constants:
+// --------------------------------------------------------------------
+
+static const char CONFIG_CXXFLAGS[] = "cpp.cxxflags";
+static const char CONFIG_DEFINES[] = "cpp.defines";
+static const char CONFIG_INCLUDEPATHS[] = "cpp.includePaths";
+static const char CONFIG_FRAMEWORKPATHS[] = "cpp.frameworkPaths";
+static const char CONFIG_PRECOMPILEDHEADER[] = "modules.cpp.precompiledHeader";
+
+static const char CONFIGURATION_PATH[] = "<configuration>";
+
+// --------------------------------------------------------------------
+// HELPERS:
+// --------------------------------------------------------------------
+
+// FIXME: All this should be in QBS! They do the same thing in JS.
+
+static const char MODULES_KEY[] = "modules";
+
+ProjectExplorer::TaskHub *taskHub()
+{
+ return ProjectExplorer::ProjectExplorerPlugin::instance()->taskHub();
+}
+
+
+static QVariant extract(const QVariantMap &data, const QString &key)
+{
+ QStringList keyParts = key.split(QLatin1Char('.'), QString::SkipEmptyParts);
+ return qbs::Internal::getConfigProperty(data, keyParts);
+}
+
+static void expand(const QVariant &v, QStringList &partial, QSet<QString> &seenSet)
+{
+ if (v.isNull())
+ return;
+
+ QStringList tokenList;
+ if (v.type() == QVariant::StringList)
+ tokenList = v.toStringList();
+ else
+ tokenList << v.toString();
+
+ foreach (const QString &token, tokenList) {
+ if (!seenSet.contains(token)) {
+ partial << token;
+ seenSet.insert(token);
+ }
+ }
+}
+
+static QVariantList modules(const QVariantMap &root)
+{
+ QVariantList result;
+ if (!root.contains(QLatin1String(MODULES_KEY)))
+ return result;
+ const QVariantMap &moduleRoot = root.value(QLatin1String(MODULES_KEY)).toMap();
+ QVariantMap::const_iterator end = moduleRoot.end();
+ for (QVariantMap::const_iterator i = moduleRoot.begin(); i != end; ++i)
+ result << i.value();
+ return result;
+}
+
+static void recursiveAppendAll(const QString &key, const QVariantMap &root,
+ QStringList &partial, QSet<QString> &seenSet)
+{
+ if (root.isEmpty())
+ return;
+ expand(extract(root, key), partial, seenSet);
+ foreach (const QVariant &v, modules(root))
+ recursiveAppendAll(key, v.toMap(), partial, seenSet);
+}
+
+static QStringList appendAll(const QVariantMap &data, const QString &key)
+{
+ QStringList result;
+ QSet<QString> seenSet;
+ recursiveAppendAll(key, data, result, seenSet);
+ return result;
+}
+
+namespace QbsProjectManager {
+namespace Internal {
+
+// --------------------------------------------------------------------
+// QbsProject:
+// --------------------------------------------------------------------
+
+QbsProject::QbsProject(QbsManager *manager, const QString &fileName) :
+ m_manager(manager), m_projectName(QFileInfo(fileName).completeBaseName()), m_fileName(fileName),
+ m_rootProjectNode(new QbsProjectNode(fileName)),
+ m_qbsSetupProjectJob(0),
+ m_qbsUpdateFutureInterface(0),
+ m_currentBc(0)
+{
+ setProjectContext(Core::Context(Constants::PROJECT_ID));
+ setProjectLanguage(Core::Context(ProjectExplorer::Constants::LANG_CXX));
+
+ connect(this, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
+ this, SLOT(changeActiveTarget(ProjectExplorer::Target*)));
+ connect(this, SIGNAL(addedTarget(ProjectExplorer::Target*)),
+ this, SLOT(targetWasAdded(ProjectExplorer::Target*)));
+
+ updateDocuments(0);
+}
+
+QbsProject::~QbsProject()
+{
+ m_codeModelFuture.cancel();
+}
+
+QString QbsProject::displayName() const
+{
+ return m_projectName;
+}
+
+Core::Id QbsProject::id() const
+{
+ return Core::Id(Constants::PROJECT_ID);
+}
+
+Core::IDocument *QbsProject::document() const
+{
+ foreach (Core::IDocument *doc, m_qbsDocuments) {
+ if (doc->fileName() == m_fileName)
+ return doc;
+ }
+ QTC_ASSERT(false, return 0);
+}
+
+QbsManager *QbsProject::projectManager() const
+{
+ return m_manager;
+}
+
+ProjectExplorer::ProjectNode *QbsProject::rootProjectNode() const
+{
+ return m_rootProjectNode;
+}
+
+QStringList QbsProject::files(ProjectExplorer::Project::FilesMode fileMode) const
+{
+ Q_UNUSED(fileMode);
+ QStringList result;
+ if (m_rootProjectNode && m_rootProjectNode->projectData()) {
+ foreach (const qbs::ProductData &prd, m_rootProjectNode->projectData()->products())
+ foreach (const qbs::GroupData &grp, prd.groups())
+ result.append(grp.allFilePaths());
+ }
+ return result;
+}
+
+void QbsProject::invalidate()
+{
+ prepareForParsing();
+}
+
+qbs::BuildJob *QbsProject::build(const qbs::BuildOptions &opts)
+{
+ if (!m_rootProjectNode || !m_rootProjectNode->project())
+ return 0;
+ if (!activeTarget() || !activeTarget()->kit())
+ return 0;
+ ProjectExplorer::BuildConfiguration *bc = 0;
+ bc = activeTarget()->activeBuildConfiguration();
+ if (!bc)
+ return 0;
+
+ QProcessEnvironment env = bc->environment().toProcessEnvironment();
+ return m_rootProjectNode->project()->buildAllProducts(opts, env);
+}
+
+qbs::CleanJob *QbsProject::clean(const qbs::BuildOptions &opts, bool everything)
+{
+ if (!m_rootProjectNode || !m_rootProjectNode->project())
+ return 0;
+ return m_rootProjectNode->project()->cleanAllProducts(opts, everything ? qbs::Project::CleanupAll
+ : qbs::Project::CleanupTemporaries);
+}
+
+QString QbsProject::profileForTarget(const ProjectExplorer::Target *t) const
+{
+ return m_manager->profileForKit(t->kit());
+}
+
+bool QbsProject::isParsing() const
+{
+ return m_qbsUpdateFutureInterface;
+}
+
+bool QbsProject::hasParseResult() const
+{
+ return m_rootProjectNode->project();
+}
+
+Utils::FileName QbsProject::defaultBuildDirectory() const
+{
+ QFileInfo fi(m_fileName);
+ const QString buildDir = QDir(fi.canonicalPath()).absoluteFilePath(QString::fromLatin1("../%1-build").arg(fi.baseName()));
+ return Utils::FileName::fromString(buildDir);
+}
+
+void QbsProject::handleQbsParsingDone(bool success)
+{
+ QTC_ASSERT(m_qbsSetupProjectJob, return);
+ QTC_ASSERT(m_qbsUpdateFutureInterface, return);
+
+ qbs::Project *project = 0;
+ if (success) {
+ project = new qbs::Project(m_qbsSetupProjectJob->project());
+ } else {
+ generateErrors(m_qbsSetupProjectJob->error());
+ m_qbsUpdateFutureInterface->reportCanceled();
+ }
+ m_qbsSetupProjectJob->deleteLater();
+ m_qbsSetupProjectJob = 0;
+
+ m_qbsUpdateFutureInterface->reportFinished();
+ delete m_qbsUpdateFutureInterface;
+ m_qbsUpdateFutureInterface = 0;
+
+ m_rootProjectNode->update(project);
+
+ updateDocuments(m_rootProjectNode->projectData());
+
+ updateCppCodeModel(m_rootProjectNode->projectData());
+ updateQmlJsCodeModel(m_rootProjectNode->projectData());
+
+ emit projectParsingDone(success);
+}
+
+void QbsProject::handleQbsParsingProgress(int progress)
+{
+ if (m_qbsUpdateFutureInterface)
+ m_qbsUpdateFutureInterface->setProgressValue(m_currentProgressBase + progress);
+}
+
+void QbsProject::handleQbsParsingTaskSetup(const QString &description, int maximumProgressValue)
+{
+ Q_UNUSED(description);
+ if (m_qbsUpdateFutureInterface) {
+ m_currentProgressBase = m_qbsUpdateFutureInterface->progressValue();
+ m_qbsUpdateFutureInterface->setProgressRange(0, m_currentProgressBase + maximumProgressValue);
+ }
+}
+
+void QbsProject::targetWasAdded(ProjectExplorer::Target *t)
+{
+ connect(t, SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)),
+ this, SLOT(parseCurrentBuildConfiguration()));
+ connect(t, SIGNAL(buildDirectoryChanged()),
+ this, SLOT(parseCurrentBuildConfiguration()));
+}
+
+void QbsProject::changeActiveTarget(ProjectExplorer::Target *t)
+{
+ ProjectExplorer::BuildConfiguration *bc = 0;
+ if (t && t->kit())
+ bc = t->activeBuildConfiguration();
+ buildConfigurationChanged(bc);
+}
+
+void QbsProject::buildConfigurationChanged(ProjectExplorer::BuildConfiguration *bc)
+{
+ if (m_currentBc)
+ disconnect(m_currentBc, SIGNAL(qbsConfigurationChanged()), this, SLOT(parseCurrentBuildConfiguration()));
+
+ m_currentBc = qobject_cast<QbsBuildConfiguration *>(bc);
+ if (m_currentBc) {
+ connect(m_currentBc, SIGNAL(qbsConfigurationChanged()), this, SLOT(parseCurrentBuildConfiguration()));
+ parseCurrentBuildConfiguration();
+ } else {
+ invalidate();
+ }
+}
+
+void QbsProject::parseCurrentBuildConfiguration()
+{
+ if (!activeTarget())
+ return;
+ QbsBuildConfiguration *bc = qobject_cast<QbsBuildConfiguration *>(activeTarget()->activeBuildConfiguration());
+ if (!bc)
+ return;
+ parse(bc->qbsConfiguration(), bc->buildDirectory());
+}
+
+bool QbsProject::fromMap(const QVariantMap &map)
+{
+ if (!Project::fromMap(map))
+ return false;
+
+ ProjectExplorer::KitManager *km = ProjectExplorer::KitManager::instance();
+ if (!activeTarget() && km->defaultKit()) {
+ ProjectExplorer::Target *t = new ProjectExplorer::Target(this, km->defaultKit());
+ t->updateDefaultBuildConfigurations();
+ t->updateDefaultDeployConfigurations();
+ t->updateDefaultRunConfigurations();
+ addTarget(t);
+ }
+
+ return true;
+}
+
+void QbsProject::generateErrors(const qbs::Error &e)
+{
+ foreach (const qbs::ErrorData &data, e.entries())
+ taskHub()->addTask(ProjectExplorer::Task(ProjectExplorer::Task::Error,
+ data.description(),
+ Utils::FileName::fromString(data.codeLocation().fileName),
+ data.codeLocation().line,
+ ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
+}
+
+void QbsProject::parse(const QVariantMap &config, const QString &dir)
+{
+ QTC_ASSERT(!dir.isNull(), return);
+ prepareForParsing();
+ m_qbsBuildConfig = config;
+ m_qbsBuildRoot = dir;
+
+ QTC_ASSERT(!m_qbsSetupProjectJob, return);
+ m_qbsSetupProjectJob
+ = qbs::Project::setupProject(m_fileName, m_qbsBuildConfig, m_qbsBuildRoot,
+ m_manager->settings(), 0);
+
+ connect(m_qbsSetupProjectJob, SIGNAL(finished(bool,qbs::AbstractJob*)),
+ this, SLOT(handleQbsParsingDone(bool)));
+ connect(m_qbsSetupProjectJob, SIGNAL(taskStarted(QString,int,qbs::AbstractJob*)),
+ this, SLOT(handleQbsParsingTaskSetup(QString,int)));
+ connect(m_qbsSetupProjectJob, SIGNAL(taskProgress(int,qbs::AbstractJob*)),
+ this, SLOT(handleQbsParsingProgress(int)));
+}
+
+void QbsProject::prepareForParsing()
+{
+ taskHub()->clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
+ if (m_qbsUpdateFutureInterface)
+ m_qbsUpdateFutureInterface->reportCanceled();
+ delete m_qbsUpdateFutureInterface;
+ m_qbsUpdateFutureInterface = 0;
+
+ // FIXME: Christian claims this should work
+ delete m_qbsSetupProjectJob;
+ m_qbsSetupProjectJob = 0;
+
+ m_currentProgressBase = 0;
+ m_qbsUpdateFutureInterface = new QFutureInterface<void>();
+ m_qbsUpdateFutureInterface->setProgressRange(0, 0);
+ Core::ICore::progressManager()->addTask(m_qbsUpdateFutureInterface->future(), tr("Evaluating"),
+ QLatin1String(Constants::QBS_EVALUATE));
+ m_qbsUpdateFutureInterface->reportStarted();
+}
+
+void QbsProject::updateDocuments(const qbs::ProjectData *prj)
+{
+ // Update documents:
+ QSet<QString> newFiles;
+ newFiles.insert(m_fileName); // make sure we always have the project file...
+
+ if (prj) {
+ newFiles.insert(prj->location().fileName);
+ foreach (const qbs::ProductData &prd, prj->products())
+ newFiles.insert(prd.location().fileName);
+ }
+ QSet<QString> oldFiles;
+ foreach (Core::IDocument *doc, m_qbsDocuments)
+ oldFiles.insert(doc->fileName());
+
+ QSet<QString> filesToAdd = newFiles;
+ filesToAdd.subtract(oldFiles);
+ QSet<QString> filesToRemove = oldFiles;
+ filesToRemove.subtract(newFiles);
+
+ QSet<Core::IDocument *> currentDocuments = m_qbsDocuments;
+ foreach (Core::IDocument *doc, currentDocuments) {
+ if (filesToRemove.contains(doc->fileName())) {
+ m_qbsDocuments.remove(doc);
+ delete doc;
+ }
+ }
+ QSet<Core::IDocument *> toAdd;
+ foreach (const QString &f, filesToAdd)
+ toAdd.insert(new QbsProjectFile(this, f));
+
+ Core::DocumentManager::instance()->addDocuments(toAdd.toList());
+ m_qbsDocuments.unite(toAdd);
+}
+
+void QbsProject::updateCppCodeModel(const qbs::ProjectData *prj)
+{
+ if (!prj)
+ return;
+
+ ProjectExplorer::Kit *k = 0;
+ QtSupport::BaseQtVersion *qtVersion = 0;
+ ProjectExplorer::ToolChain *tc = 0;
+ if (ProjectExplorer::Target *target = activeTarget())
+ k = target->kit();
+ else
+ k = ProjectExplorer::KitManager::instance()->defaultKit();
+ qtVersion = QtSupport::QtKitInformation::qtVersion(k);
+ tc = ProjectExplorer::ToolChainKitInformation::toolChain(k);
+
+ CPlusPlus::CppModelManagerInterface *modelmanager =
+ CPlusPlus::CppModelManagerInterface::instance();
+
+ if (!modelmanager)
+ return;
+
+ CPlusPlus::CppModelManagerInterface::ProjectInfo pinfo = modelmanager->projectInfo(this);
+ pinfo.clearProjectParts();
+ CPlusPlus::CppModelManagerInterface::ProjectPart::QtVersion qtVersionForPart
+ = CPlusPlus::CppModelManagerInterface::ProjectPart::NoQt;
+ if (qtVersion) {
+ if (qtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0))
+ qtVersionForPart = CPlusPlus::CppModelManagerInterface::ProjectPart::Qt4;
+ else
+ qtVersionForPart = CPlusPlus::CppModelManagerInterface::ProjectPart::Qt5;
+ }
+
+ QStringList allFiles;
+ foreach (const qbs::ProductData &prd, prj->products()) {
+ foreach (const qbs::GroupData &grp, prd.groups()) {
+ QVariantMap props = grp.properties();
+
+ QStringList grpIncludePaths;
+ QStringList grpFrameworkPaths;
+ QByteArray grpDefines;
+ bool isCxx11;
+ const QStringList cxxFlags = appendAll(props, QLatin1String(CONFIG_CXXFLAGS));
+
+ // Toolchain specific stuff:
+ QList<ProjectExplorer::HeaderPath> includePaths;
+ if (tc) {
+ includePaths = tc->systemHeaderPaths(cxxFlags, ProjectExplorer::SysRootKitInformation::sysRoot(k));
+ grpDefines += tc->predefinedMacros(cxxFlags);
+ if (tc->compilerFlags(cxxFlags) == ProjectExplorer::ToolChain::STD_CXX11)
+ isCxx11 = true;
+ }
+ foreach (const ProjectExplorer::HeaderPath &headerPath, includePaths) {
+ if (headerPath.kind() == ProjectExplorer::HeaderPath::FrameworkHeaderPath)
+ grpFrameworkPaths.append(headerPath.path());
+ else
+ grpIncludePaths.append(headerPath.path());
+ }
+
+ QStringList list = appendAll(props, QLatin1String(CONFIG_DEFINES));
+ foreach (const QString &def, list)
+ grpDefines += (QByteArray("#define ") + def.toUtf8() + '\n');
+
+ list = appendAll(props, QLatin1String(CONFIG_INCLUDEPATHS));
+ foreach (const QString &p, list) {
+ const QString cp = Utils::FileName::fromUserInput(p).toString();
+ grpIncludePaths.append(cp);
+ }
+
+ list = appendAll(props, QLatin1String(CONFIG_FRAMEWORKPATHS));
+ foreach (const QString &p, list) {
+ const QString cp = Utils::FileName::fromUserInput(p).toString();
+ grpFrameworkPaths.append(cp);
+ }
+
+ const QString pch = extract(props, QLatin1String(CONFIG_PRECOMPILEDHEADER)).toString();
+
+ QStringList cxxSources;
+ QStringList cSources;
+ QStringList headers;
+ QStringList objcSources;
+ cxxSources << QLatin1String(CONFIGURATION_PATH);
+ cSources << QLatin1String(CONFIGURATION_PATH);
+ objcSources << QLatin1String(CONFIGURATION_PATH);
+
+ foreach (const QString &file, grp.allFilePaths()) {
+ QFileInfo fi = QFileInfo(file);
+ if (!fi.exists())
+ continue;
+
+ Core::MimeType t = Core::ICore::mimeDatabase()->findByFile(fi);
+ if (t.isNull())
+ continue;
+ if (t.matchesType(QLatin1String("text/x-chdr")))
+ headers << file;
+ else if (t.matchesType(QLatin1String("text/x-c++src")))
+ cxxSources << file;
+ else if (t.matchesType(QLatin1String("text/x-objcsrc")))
+ objcSources << file;
+ else if (t.matchesType(QLatin1String("text/x-csrc")))
+ cxxSources << file;
+ }
+ allFiles.append(headers);
+ allFiles.append(cSources);
+ allFiles.append(cxxSources);
+ allFiles.append(objcSources);
+
+ if (cxxSources.count() > 1) {
+ CPlusPlus::CppModelManagerInterface::ProjectPart::Ptr part(new CPlusPlus::CppModelManagerInterface::ProjectPart);
+ part->qtVersion = qtVersionForPart;
+ part->language = isCxx11 ? CPlusPlus::CppModelManagerInterface::ProjectPart::CXX11
+ : CPlusPlus::CppModelManagerInterface::ProjectPart::CXX;
+ part->sourceFiles = cxxSources;
+ part->objcSourceFiles = objcSources;
+ part->headerFiles = headers;
+ part->includePaths = grpIncludePaths;
+ part->frameworkPaths = grpFrameworkPaths;
+ part->precompiledHeaders = QStringList(pch);
+ part->defines = grpDefines;
+ pinfo.appendProjectPart(part);
+ }
+ if (cSources.count() > 1) {
+ CPlusPlus::CppModelManagerInterface::ProjectPart::Ptr part(new CPlusPlus::CppModelManagerInterface::ProjectPart);
+ part->qtVersion = CPlusPlus::CppModelManagerInterface::ProjectPart::NoQt;
+ part->language = CPlusPlus::CppModelManagerInterface::ProjectPart::C99; // FIXME: Can we find the exact c version from tc?
+ part->sourceFiles = cxxSources;
+ part->headerFiles = headers;
+ part->includePaths = grpIncludePaths;
+ part->frameworkPaths = grpFrameworkPaths;
+ part->precompiledHeaders = QStringList(pch);
+ part->defines = grpDefines;
+ pinfo.appendProjectPart(part);
+ }
+ }
+ }
+
+ if (pinfo.projectParts().isEmpty())
+ return;
+
+ // Register update the code model:
+ modelmanager->updateProjectInfo(pinfo);
+ m_codeModelFuture = modelmanager->updateSourceFiles(allFiles);
+}
+
+void QbsProject::updateQmlJsCodeModel(const qbs::ProjectData *prj)
+{
+ // FIXME: No information about import directories, so ignore this for now.
+#if 1
+ Q_UNUSED(prj);
+#else
+ QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
+ if (!modelManager)
+ return;
+
+ QmlJS::ModelManagerInterface::ProjectInfo projectInfo = modelManager->projectInfo(this);
+ projectInfo.sourceFiles = m_projectFiles->files[QMLType];
+
+ FindQt4ProFiles findQt4ProFiles;
+ QList<Qt4ProFileNode *> proFiles = findQt4ProFiles(rootProjectNode());
+
+ projectInfo.importPaths.clear();
+ foreach (Qt4ProFileNode *node, proFiles) {
+ projectInfo.importPaths.append(node->variableValue(QmlImportPathVar));
+ }
+
+ bool preferDebugDump = false;
+ projectInfo.tryQmlDump = false;
+
+ ProjectExplorer::Target *t = activeTarget();
+ ProjectExplorer::Kit *k = t ? t->kit() : ProjectExplorer::KitManager::instance()->defaultKit();
+ QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k);
+
+ if (t) {
+ if (Qt4BuildConfiguration *bc = qobject_cast<Qt4BuildConfiguration *>(t->activeBuildConfiguration()))
+ preferDebugDump = bc->qmakeBuildConfiguration() & QtSupport::BaseQtVersion::DebugBuild;
+ } else {
+ if (qtVersion)
+ preferDebugDump = qtVersion->defaultBuildConfig() & QtSupport::BaseQtVersion::DebugBuild;
+ }
+ if (qtVersion && qtVersion->isValid()) {
+ projectInfo.tryQmlDump = qtVersion->type() == QLatin1String(QtSupport::Constants::DESKTOPQT)
+ || qtVersion->type() == QLatin1String(QtSupport::Constants::SIMULATORQT);
+ projectInfo.qtImportsPath = qtVersion->qmakeProperty("QT_INSTALL_IMPORTS");
+ if (!projectInfo.qtImportsPath.isEmpty())
+ projectInfo.importPaths += projectInfo.qtImportsPath;
+ projectInfo.qtVersionString = qtVersion->qtVersionString();
+ }
+ projectInfo.importPaths.removeDuplicates();
+
+ if (projectInfo.tryQmlDump) {
+ QtSupport::QmlDumpTool::pathAndEnvironment(this, qtVersion,
+ ToolChainKitInformation::toolChain(k),
+ preferDebugDump, &projectInfo.qmlDumpPath,
+ &projectInfo.qmlDumpEnvironment);
+ } else {
+ projectInfo.qmlDumpPath.clear();
+ projectInfo.qmlDumpEnvironment.clear();
+ }
+
+ modelManager->updateProjectInfo(projectInfo);
+#endif
+}
+
+} // namespace Internal
+} // namespace QbsProjectManager
diff --git a/src/plugins/qbsprojectmanager/qbsproject.h b/src/plugins/qbsprojectmanager/qbsproject.h
new file mode 100644
index 0000000000..b9fbd08129
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsproject.h
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** 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 QBSPROJECT_H
+#define QBSPROJECT_H
+
+#include "qbsprojectmanager.h"
+
+#include <language/language.h>
+#include <tools/buildoptions.h>
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectnodes.h>
+#include <projectexplorer/task.h>
+
+#include <QFuture>
+// <debug>
+#include <QTimer>
+// </debug>
+#include <QVariantMap>
+
+namespace qbs {
+class BuildJob;
+class CleanJob;
+class Error;
+class ProjectData;
+class SetupProjectJob;
+} // namespace qbs
+
+namespace Core { class IDocument; }
+namespace ProjectExplorer { class BuildConfiguration; }
+
+namespace QbsProjectManager {
+namespace Internal {
+
+class QbsProjectNode;
+class QbsBuildConfiguration;
+
+class QbsProject : public ProjectExplorer::Project
+{
+ Q_OBJECT
+
+public:
+ QbsProject(QbsManager *manager, const QString &filename);
+ ~QbsProject();
+
+ QString displayName() const;
+ Core::Id id() const;
+ Core::IDocument *document() const;
+ QbsManager *projectManager() const;
+
+ ProjectExplorer::ProjectNode *rootProjectNode() const;
+
+ QStringList files(FilesMode fileMode) const;
+
+ qbs::BuildJob *build(const qbs::BuildOptions &opts);
+ qbs::CleanJob *clean(const qbs::BuildOptions &opts, bool everything);
+
+ static ProjectExplorer::FileType fileTypeFor(const QSet<QString> &tags);
+
+ QString profileForTarget(const ProjectExplorer::Target *t) const;
+ bool isParsing() const;
+ bool hasParseResult() const;
+
+ Utils::FileName defaultBuildDirectory() const;
+
+public slots:
+ void invalidate();
+ void parseCurrentBuildConfiguration();
+
+signals:
+ void projectParsingStarted();
+ void projectParsingDone(bool);
+
+private slots:
+ void handleQbsParsingDone(bool success);
+ void handleQbsParsingProgress(int progress);
+ void handleQbsParsingTaskSetup(const QString &description, int maximumProgressValue);
+
+ void targetWasAdded(ProjectExplorer::Target *t);
+ void changeActiveTarget(ProjectExplorer::Target *t);
+ void buildConfigurationChanged(ProjectExplorer::BuildConfiguration *bc);
+
+private:
+ bool fromMap(const QVariantMap &map);
+
+ void parse(const QVariantMap &config, const QString &dir = QString());
+
+ void generateErrors(const qbs::Error &e);
+ void prepareForParsing();
+ void updateDocuments(const qbs::ProjectData *prj);
+ void updateCppCodeModel(const qbs::ProjectData *prj);
+ void updateQmlJsCodeModel(const qbs::ProjectData *prj);
+
+ QbsManager *const m_manager;
+ const QString m_projectName;
+ const QString m_fileName;
+ QSet<Core::IDocument *> m_qbsDocuments;
+ QbsProjectNode *const m_rootProjectNode;
+
+ qbs::SetupProjectJob *m_qbsSetupProjectJob;
+ QVariantMap m_qbsBuildConfig;
+ QString m_qbsBuildRoot;
+
+ QFutureInterface<void> *m_qbsUpdateFutureInterface;
+ int m_currentProgressBase;
+
+ QFuture<void> m_codeModelFuture;
+
+ QbsBuildConfiguration *m_currentBc;
+};
+
+} // namespace Internal
+} // namespace QbsProjectManager
+
+#endif // QBSPROJECT_H
diff --git a/src/plugins/qbsprojectmanager/qbsprojectfile.cpp b/src/plugins/qbsprojectmanager/qbsprojectfile.cpp
new file mode 100644
index 0000000000..3622a9ddde
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsprojectfile.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "qbsprojectfile.h"
+
+#include "qbsproject.h"
+#include "qbsprojectmanagerconstants.h"
+
+namespace QbsProjectManager {
+namespace Internal {
+
+QbsProjectFile::QbsProjectFile(QbsProject *parent, QString fileName) : Core::IDocument(parent),
+ m_project(parent),
+ m_fileName(fileName)
+{ }
+
+QbsProjectFile::~QbsProjectFile()
+{ }
+
+bool QbsProjectFile::save(QString *, const QString &, bool)
+{
+ return false;
+}
+
+QString QbsProjectFile::fileName() const
+{
+ return m_fileName;
+}
+
+bool QbsProjectFile::isReadOnly() const
+{
+ return true;
+}
+
+QString QbsProjectFile::defaultPath() const
+{
+ return QString();
+}
+
+QString QbsProjectFile::suggestedFileName() const
+{
+ return QString();
+}
+
+QString QbsProjectFile::mimeType() const
+{
+ return QLatin1String(Constants::MIME_TYPE);
+}
+
+bool QbsProjectFile::isModified() const
+{
+ return false;
+}
+
+bool QbsProjectFile::isSaveAsAllowed() const
+{
+ return false;
+}
+
+Core::IDocument::ReloadBehavior QbsProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
+{
+ Q_UNUSED(state);
+ Q_UNUSED(type);
+ return BehaviorSilent;
+}
+
+bool QbsProjectFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
+{
+ Q_UNUSED(errorString)
+ Q_UNUSED(flag)
+ if (type == TypePermissions)
+ return true;
+ m_project->parseCurrentBuildConfiguration();
+ return true;
+}
+
+void QbsProjectFile::rename(const QString &newName)
+{
+ // Can't happen
+ Q_UNUSED(newName);
+ Q_ASSERT(false);
+}
+
+} // namespace Internal
+} // namespace QbsProjectManager
+
diff --git a/src/plugins/qbsprojectmanager/qbsprojectfile.h b/src/plugins/qbsprojectmanager/qbsprojectfile.h
new file mode 100644
index 0000000000..bc45062b3c
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsprojectfile.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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 QBSPROJECTFILE_H
+#define QBSPROJECTFILE_H
+
+#include <coreplugin/idocument.h>
+
+namespace QbsProjectManager {
+namespace Internal {
+
+class QbsProject;
+
+class QbsProjectFile : public Core::IDocument
+{
+ Q_OBJECT
+
+public:
+ QbsProjectFile(QbsProject *parent, QString fileName);
+ ~QbsProjectFile();
+
+ bool save(QString *errorString, const QString &fileName, bool autoSave);
+ QString fileName() const;
+ bool isReadOnly() const;
+
+ QString defaultPath() const;
+ QString suggestedFileName() const;
+ QString mimeType() const;
+
+ bool isModified() const;
+ bool isSaveAsAllowed() const;
+
+ ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
+ bool reload(QString *errorString, ReloadFlag flag, ChangeType type);
+ void rename(const QString &newName);
+
+private:
+ QbsProject *m_project;
+ QString m_fileName;
+};
+
+} // namespace Internal
+} // namespace QbsProjectManager
+
+#endif // QBSPROJECTFILE_H
diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager.cpp b/src/plugins/qbsprojectmanager/qbsprojectmanager.cpp
new file mode 100644
index 0000000000..55b46e526c
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsprojectmanager.cpp
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "qbsprojectmanager.h"
+
+#include "qbslogsink.h"
+#include "qbsproject.h"
+#include "qbsprojectmanagerconstants.h"
+#include "qbsprojectmanagerplugin.h"
+
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/kitmanager.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/toolchain.h>
+#include <qmljstools/qmljstoolsconstants.h>
+#include <qtsupport/baseqtversion.h>
+#include <qtsupport/qtkitinformation.h>
+#include <utils/qtcassert.h>
+
+#include <QVariantMap>
+
+#include <qbs.h>
+
+// qbs settings structure:
+const char PROFILE_LIST[] = "preferences.qtcreator.kit.";
+const char PROFILES_PREFIX[] = "profiles.";
+
+// Qt related settings:
+const char QTCORE_BINPATH[] = ".qt.core.binPath";
+const char QTCORE_INCPATH[] = ".qt.core.incPath";
+const char QTCORE_LIBPATH[] = ".qt.core.libPath";
+const char QTCORE_VERSION[] = ".qt.core.version";
+const char QTCORE_NAMESPACE[] = ".qt.core.namespace";
+const char QTCORE_LIBINFIX[] = ".qt.core.libInfix";
+const char QTCORE_MKSPEC[] = ".qt.core.mkspecPath";
+
+
+// Toolchain related settings:
+const char QBS_TARGETOS[] = ".qbs.targetOS";
+const char QBS_SYSROOT[] = ".qbs.sysroot";
+const char QBS_ARCHITECTURE[] = ".qbs.architecture";
+const char QBS_TOOLCHAIN[] = ".qbs.toolchain";
+const char CPP_TOOLCHAINPATH[] = ".cpp.toolchainInstallPath";
+const char CPP_COMPILERNAME[] = ".cpp.compilerName";
+
+const QChar sep = QChar(QLatin1Char('.'));
+
+namespace QbsProjectManager {
+
+qbs::Settings *QbsManager::m_settings = new qbs::Settings(QLatin1String("QtProject"), QLatin1String("qbs"));
+
+QbsManager::QbsManager(Internal::QbsProjectManagerPlugin *plugin) :
+ m_plugin(plugin)
+{
+ setObjectName(QLatin1String("QbsProjectManager"));
+ connect(ProjectExplorer::KitManager::instance(), SIGNAL(kitsChanged()), this, SLOT(pushKitsToQbs()));
+
+ qbs::Logger::instance().setLogSink(new Internal::QbsLogSink);
+ qbs::Logger::instance().setLevel(qbs::LoggerWarning);
+}
+
+QbsManager::~QbsManager()
+{
+ delete m_settings;
+}
+
+QString QbsManager::mimeType() const
+{
+ return QLatin1String(QmlJSTools::Constants::QBS_MIMETYPE);
+}
+
+ProjectExplorer::Project *QbsManager::openProject(const QString &fileName, QString *errorString)
+{
+ Q_UNUSED(errorString);
+
+ // FIXME: This is way too simplistic!
+ return new Internal::QbsProject(this, fileName);
+}
+
+QString QbsManager::profileForKit(const ProjectExplorer::Kit *k) const
+{
+ if (!k)
+ return QString();
+ return m_settings->value(QString::fromLatin1(PROFILE_LIST) + k->id().toString()).toString();
+}
+
+void QbsManager::setProfileForKit(const QString &name, const ProjectExplorer::Kit *k)
+{
+ m_settings->setValue(QString::fromLatin1(PROFILE_LIST) + k->id().toString(), name);
+}
+
+QStringList QbsManager::profileNames() const
+{
+ QStringList keyList = m_settings->allKeys();
+
+ QStringList result;
+ foreach (const QString &key, keyList) {
+ if (!key.startsWith(QString::fromLatin1(PROFILES_PREFIX)))
+ continue;
+ QString profile = key;
+ profile.remove(0, QString::fromLatin1(PROFILES_PREFIX).count());
+ profile = profile.left(profile.indexOf(sep));
+ if (!result.contains(profile))
+ result << profile;
+ }
+ return result;
+}
+
+qbs::Settings *QbsManager::settings()
+{
+ return m_settings;
+}
+
+void QbsManager::addProfile(const QString &name, const QVariantMap &data)
+{
+ const QString base = QLatin1String(PROFILES_PREFIX) + name;
+ foreach (const QString &key, data.keys())
+ m_settings->setValue(base + key, data.value(key));
+}
+
+void QbsManager::removeCreatorProfiles()
+{
+ QStringList keyList = m_settings->allKeys();
+ QStringList profilesToDelete;
+
+ // Find profiles to remove:
+ foreach (const QString &key, keyList) {
+ if (!key.startsWith(QLatin1String(PROFILE_LIST)))
+ continue;
+ profilesToDelete.append(m_settings->value(key).toString());
+ m_settings->remove(key);
+ }
+ // Remove profiles:
+ foreach (const QString &key, keyList) {
+ if (!key.startsWith(QLatin1String(PROFILES_PREFIX)))
+ continue;
+ const QString kitname = key.mid(QString::fromLatin1(PROFILES_PREFIX).size());
+ foreach (const QString &i, profilesToDelete) {
+ if (kitname.startsWith(i + sep))
+ m_settings->remove(key);
+ }
+ }
+}
+
+void QbsManager::addProfileFromKit(const ProjectExplorer::Kit *k)
+{
+ QStringList usedProfileNames = profileNames();
+ const QString name = ProjectExplorer::Project::makeUnique(k->fileSystemFriendlyName(), usedProfileNames);
+ setProfileForKit(name, k);
+
+ QVariantMap data;
+ QtSupport::BaseQtVersion *qt = QtSupport::QtKitInformation::qtVersion(k);
+ if (qt) {
+ data.insert(QLatin1String(QTCORE_BINPATH), qt->binPath().toUserOutput());
+ data.insert(QLatin1String(QTCORE_INCPATH), qt->headerPath().toUserOutput());
+ data.insert(QLatin1String(QTCORE_LIBPATH), qt->libraryPath().toUserOutput());
+ data.insert(QLatin1String(QTCORE_MKSPEC), qt->mkspecsPath().toUserOutput());
+ data.insert(QLatin1String(QTCORE_NAMESPACE), qt->qtNamespace());
+ data.insert(QLatin1String(QTCORE_LIBINFIX), qt->qtLibInfix());
+ data.insert(QLatin1String(QTCORE_VERSION), qt->qtVersionString());
+ }
+
+ if (ProjectExplorer::SysRootKitInformation::hasSysRoot(k))
+ data.insert(QLatin1String(QBS_SYSROOT), ProjectExplorer::SysRootKitInformation::sysRoot(k).toUserOutput());
+
+ ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(k);
+ if (tc) {
+ // FIXME/CLARIFY: How to pass the sysroot?
+ ProjectExplorer::Abi targetAbi = tc->targetAbi();
+ QString architecture = ProjectExplorer::Abi::toString(targetAbi.architecture());
+ if (targetAbi.wordWidth() == 64)
+ architecture.append(QLatin1String("_64"));
+ data.insert(QLatin1String(QBS_ARCHITECTURE), architecture);
+
+ if (targetAbi.os() == ProjectExplorer::Abi::WindowsOS) {
+ data.insert(QLatin1String(QBS_TARGETOS), QLatin1String("windows"));
+ data.insert(QLatin1String(QBS_TOOLCHAIN),
+ targetAbi.osFlavor() == ProjectExplorer::Abi::WindowsMSysFlavor ?
+ QLatin1String("mingw") : QLatin1String("msvc"));
+ } else if (targetAbi.os() == ProjectExplorer::Abi::MacOS) {
+ data.insert(QLatin1String(QBS_TARGETOS), QLatin1String("mac"));
+ data.insert(QLatin1String(QBS_TOOLCHAIN), QLatin1String("gcc"));
+ } else if (targetAbi.os() == ProjectExplorer::Abi::LinuxOS) {
+ data.insert(QLatin1String(QBS_TARGETOS), QLatin1String("linux"));
+ data.insert(QLatin1String(QBS_TOOLCHAIN), QLatin1String("gcc"));
+ }
+ Utils::FileName cxx = tc->compilerCommand();
+ data.insert(QLatin1String(CPP_TOOLCHAINPATH), cxx.toFileInfo().absolutePath());
+ data.insert(QLatin1String(CPP_COMPILERNAME), cxx.toFileInfo().fileName());
+ }
+ addProfile(name, data);
+}
+
+void QbsManager::pushKitsToQbs()
+{
+ // Get all keys
+ removeCreatorProfiles();
+
+ // add definitions from our kits
+ foreach (const ProjectExplorer::Kit *k, ProjectExplorer::KitManager::instance()->kits())
+ addProfileFromKit(k);
+}
+
+} // namespace QbsProjectManager
diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager.h b/src/plugins/qbsprojectmanager/qbsprojectmanager.h
new file mode 100644
index 0000000000..2545211c08
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsprojectmanager.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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 QBSPROJECTMANAGER_H
+#define QBSPROJECTMANAGER_H
+
+#include "qbsprojectmanager_global.h"
+
+#include <projectexplorer/iprojectmanager.h>
+
+#include <qbs.h>
+
+#include <QString>
+
+namespace ProjectExplorer {
+class Kit;
+class Project;
+class ProjectExplorerPlugin;
+} // namespace ProjectExplorer
+
+namespace QbsProjectManager {
+
+namespace Internal {
+class QbsProject;
+class QbsProjectManagerPlugin;
+} // namespace Internal
+
+class QBSPROJECTMANAGER_EXPORT QbsManager : public ProjectExplorer::IProjectManager
+{
+ Q_OBJECT
+
+public:
+ QbsManager(Internal::QbsProjectManagerPlugin *plugin);
+ ~QbsManager();
+
+ QString mimeType() const;
+ ProjectExplorer::Project *openProject(const QString &fileName, QString *errorString);
+
+ // QBS settings management:
+ QString profileForKit(const ProjectExplorer::Kit *k) const;
+ void setProfileForKit(const QString &name, const ProjectExplorer::Kit *k);
+ QStringList profileNames() const;
+
+ static qbs::Settings *settings();
+
+private slots:
+ void pushKitsToQbs();
+
+private:
+
+ void addProfile(const QString &name, const QVariantMap &data);
+ void removeCreatorProfiles();
+ void addProfileFromKit(const ProjectExplorer::Kit *k);
+
+ Internal::QbsProjectManagerPlugin *m_plugin;
+ static qbs::Settings *m_settings;
+};
+
+} // namespace QbsProjectManager
+
+#endif // QBSPROJECTMANAGER_H
diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager.pro b/src/plugins/qbsprojectmanager/qbsprojectmanager.pro
new file mode 100644
index 0000000000..9563abc6fb
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsprojectmanager.pro
@@ -0,0 +1,53 @@
+TEMPLATE = lib
+TARGET = QbsProjectManager
+
+include(../../qtcreatorplugin.pri)
+include(../../libs/qmljs/qmljs.pri)
+include(qbsprojectmanager_dependencies.pri)
+
+# Look for qbs in the environment (again)
+isEmpty(QBS_SOURCE_DIR): QBS_SOURCE_DIR = $$(QBS_SOURCE_DIR)
+isEmpty(QBS_BUILD_DIR): QBS_BUILD_DIR = $$(QBS_BUILD_DIR)
+
+QBSLIBDIR = $$QBS_BUILD_DIR/lib
+include($$QBS_SOURCE_DIR/src/lib/use.pri)
+
+DEFINES += QBS_SOURCE_DIR=\\\"$$QBS_SOURCE_DIR\\\"
+DEFINES += QBS_BUILD_DIR=\\\"$$QBS_BUILD_DIR\\\"
+DEFINES += \
+ QT_CREATOR \
+ QBSPROJECTMANAGER_LIBRARY \
+ QT_NO_CAST_FROM_ASCII
+
+HEADERS = \
+ qbsbuildconfiguration.h \
+ qbsbuildconfigurationwidget.h \
+ qbsbuildstep.h \
+ qbscleanstep.h \
+ qbslogsink.h \
+ qbsnodes.h \
+ qbsparser.h \
+ qbsproject.h \
+ qbsprojectfile.h \
+ qbsprojectmanager.h \
+ qbsprojectmanager_global.h \
+ qbsprojectmanagerconstants.h \
+ qbsprojectmanagerplugin.h
+
+SOURCES = \
+ qbsbuildconfiguration.cpp \
+ qbsbuildconfigurationwidget.cpp \
+ qbsbuildstep.cpp \
+ qbscleanstep.cpp \
+ qbslogsink.cpp \
+ qbsnodes.cpp \
+ qbsparser.cpp \
+ qbsproject.cpp \
+ qbsprojectfile.cpp \
+ qbsprojectmanager.cpp \
+ qbsprojectmanagerplugin.cpp
+
+FORMS = \
+ qbsbuildstepconfigwidget.ui \
+ qbscleanstepconfigwidget.ui
+
diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs b/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs
new file mode 100644
index 0000000000..61e9c539e4
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs
@@ -0,0 +1,82 @@
+import qbs.base 1.0
+
+import "../QtcPlugin.qbs" as QtcPlugin
+
+QtcPlugin {
+ name: "QbsProjectManager"
+
+ property var qbs_source_dir: qbs.getenv("QBS_SOURCE_DIR")
+ property var qbs_build_dir: qbs.getenv("QBS_BUILD_DIR")
+
+ condition: qbs_source_dir !== undefined && qbs_build_dir !== undefined
+
+ Depends { name: "Qt"; submodules: [ "widgets", "script" ] }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "Core" }
+ Depends { name: "CppTools" }
+ Depends { name: "ExtensionSystem" }
+ Depends { name: "TextEditor" }
+ Depends { name: "QtSupport" }
+ Depends { name: "QmlJS" }
+
+ Depends { name: "cpp" }
+
+ cpp.includePaths: base.concat([
+ qbs_source_dir + "/src",
+ qbs_source_dir + "/src/lib",
+ ])
+
+ cpp.defines: base.concat([
+ 'QBS_SOURCE_DIR="' + qbs_source_dir + '"',
+ 'QBS_BUILD_DIR="' + qbs_build_dir +'"',
+ 'QML_BUILD_STATIC_LIB'
+ ])
+
+ cpp.staticLibraries: {
+ if (qbs.targetOS === "windows") {
+ if (qbs.enableDebugCode) {
+ return qbs_build_dir + "/lib/qbscored.lib"
+ } else {
+ return qbs_build_dir + "/lib/qbscore.lib"
+ }
+ } else {
+ return qbs_build_dir + "/lib/libqbscore.a"
+ }
+ }
+
+ cpp.dynamicLibraries: {
+ if (qbs.targetOS === "windows") {
+ return "shell32"
+ }
+ }
+
+ files: [
+ "qbsbuildconfiguration.cpp",
+ "qbsbuildconfiguration.h",
+ "qbsbuildconfigurationwidget.cpp",
+ "qbsbuildconfigurationwidget.h",
+ "qbsbuildstep.cpp",
+ "qbsbuildstep.h",
+ "qbsbuildstepconfigwidget.ui",
+ "qbscleanstep.cpp",
+ "qbscleanstep.h",
+ "qbscleanstepconfigwidget.ui",
+ "qbslogsink.cpp",
+ "qbslogsink.h",
+ "qbsnodes.cpp",
+ "qbsnodes.h",
+ "qbsparser.cpp",
+ "qbsparser.h",
+ "qbsproject.cpp",
+ "qbsproject.h",
+ "qbsprojectfile.cpp",
+ "qbsprojectfile.h",
+ "qbsprojectmanager.cpp",
+ "qbsprojectmanager.h",
+ "qbsprojectmanager_global.h",
+ "qbsprojectmanagerconstants.h",
+ "qbsprojectmanagerplugin.cpp",
+ "qbsprojectmanagerplugin.h",
+ ]
+}
+
diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager_dependencies.pri b/src/plugins/qbsprojectmanager/qbsprojectmanager_dependencies.pri
new file mode 100644
index 0000000000..03f29efcdb
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsprojectmanager_dependencies.pri
@@ -0,0 +1,4 @@
+include(../../plugins/projectexplorer/projectexplorer.pri)
+include(../../plugins/cpptools/cpptools.pri)
+include(../../plugins/texteditor/texteditor.pri)
+include(../../plugins/qtsupport/qtsupport.pri)
diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager_global.h b/src/plugins/qbsprojectmanager/qbsprojectmanager_global.h
new file mode 100644
index 0000000000..e0bf02d3e0
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsprojectmanager_global.h
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** 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 QBSPROJECTMANAGER_GLOBAL_H
+#define QBSPROJECTMANAGER_GLOBAL_H
+
+#include <qglobal.h>
+
+#if defined(QBSPROJECTMANAGER_LIBRARY)
+# define QBSPROJECTMANAGER_EXPORT Q_DECL_EXPORT
+#else
+# define QBSPROJECTMANAGER_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // QBSPROJECTMANAGER_GLOBAL_H
diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerconstants.h b/src/plugins/qbsprojectmanager/qbsprojectmanagerconstants.h
new file mode 100644
index 0000000000..d011bc4ad2
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerconstants.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 QBSPROJECTMANAGERCONSTANTS_H
+#define QBSPROJECTMANAGERCONSTANTS_H
+
+#include <QtGlobal>
+
+namespace QbsProjectManager {
+namespace Constants {
+
+// Contexts
+const char PROJECT_ID[] = "Qbs.QbsProject";
+
+// MIME types:
+const char MIME_TYPE[] = "application/vnd.qtproject.qbsproject";
+
+// Progress reports:
+const char QBS_EVALUATE[] = "Qbs.QbsEvaluate";
+
+// Actions:
+const char ACTION_REPARSE_QBS[] = "Qbs.Reparse";
+const char ACTION_REPARSE_QBS_CONTEXT[] = "Qbs.ReparseCtx";
+const char ACTION_BUILD_FILE_QBS_CONTEXT[] = "Qbs.BuildFileCtx";
+
+// Ids:
+const char QBS_BUILDSTEP_ID[] = "Qbs.BuildStep";
+const char QBS_CLEANSTEP_ID[] = "Qbs.CleanStep";
+
+// QBS strings:
+static const char QBS_VARIANT_DEBUG[] = "debug";
+static const char QBS_VARIANT_RELEASE[] = "release";
+
+static const char QBS_CONFIG_VARIANT_KEY[] = "qbs.buildVariant";
+static const char QBS_CONFIG_PROFILE_KEY[] = "qbs.profile";
+
+} // namespace Constants
+} // namespace QbsProjectManager
+
+#endif // QBSPROJECTMANAGERCONSTANTS_H
+
diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp
new file mode 100644
index 0000000000..33f868a609
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "qbsprojectmanagerplugin.h"
+
+#include "qbsbuildconfiguration.h"
+#include "qbsbuildstep.h"
+#include "qbscleanstep.h"
+#include "qbsproject.h"
+#include "qbsprojectmanager.h"
+#include "qbsprojectmanagerconstants.h"
+
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/mimedatabase.h>
+#include <projectexplorer/buildmanager.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
+#include <utils/qtcassert.h>
+
+#include <QAction>
+#include <QtPlugin>
+
+namespace QbsProjectManager {
+namespace Internal {
+
+QbsProjectManagerPlugin::QbsProjectManagerPlugin() :
+ m_manager(0),
+ m_projectExplorer(0),
+ m_currentProject(0),
+ m_currentTarget(0),
+ m_currentNode(0)
+{ }
+
+bool QbsProjectManagerPlugin::initialize(const QStringList &arguments, QString *errorMessage)
+{
+ m_manager = new QbsManager(this);
+ m_projectExplorer = ProjectExplorer::ProjectExplorerPlugin::instance();
+ const Core::Context projectContext(::QbsProjectManager::Constants::PROJECT_ID);
+
+ Q_UNUSED(arguments);
+
+ //create and register objects
+ addAutoReleasedObject(m_manager);
+ addAutoReleasedObject(new QbsBuildConfigurationFactory);
+ addAutoReleasedObject(new QbsBuildStepFactory);
+ addAutoReleasedObject(new QbsCleanStepFactory);
+
+ //menus
+ // Build Menu:
+ Core::ActionContainer *mbuild =
+ Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
+ // PE Context menu for projects
+ Core::ActionContainer *mproject =
+ Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_PROJECTCONTEXT);
+ // <debug>
+ // Core::ActionContainer *msubproject =
+ // Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_SUBPROJECTCONTEXT);
+ // </debug>
+ Core::ActionContainer *mfile =
+ Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_FILECONTEXT);
+
+
+ //register actions
+ Core::Command *command;
+
+ m_reparseQbs = new QAction(tr("Reparse Qbs"), this);
+ command = Core::ActionManager::registerAction(m_reparseQbs, Constants::ACTION_REPARSE_QBS, projectContext);
+ command->setAttribute(Core::Command::CA_Hide);
+ mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_BUILD);
+ connect(m_reparseQbs, SIGNAL(triggered()), this, SLOT(reparseCurrentProject()));
+
+ m_reparseQbsCtx = new QAction(tr("Reparse Qbs"), this);
+ command = Core::ActionManager::registerAction(m_reparseQbsCtx, Constants::ACTION_REPARSE_QBS_CONTEXT, projectContext);
+ command->setAttribute(Core::Command::CA_Hide);
+ mproject->addAction(command, ProjectExplorer::Constants::G_PROJECT_BUILD);
+ connect(m_reparseQbsCtx, SIGNAL(triggered()), this, SLOT(reparseCurrentProject()));
+
+ m_buildFileContextMenu = new QAction(tr("Build"), this);
+ command = Core::ActionManager::registerAction(m_buildFileContextMenu, Constants::ACTION_BUILD_FILE_QBS_CONTEXT, projectContext);
+ command->setAttribute(Core::Command::CA_Hide);
+ mfile->addAction(command, ProjectExplorer::Constants::G_FILE_OTHER);
+ connect(m_buildFileContextMenu, SIGNAL(triggered()), this, SLOT(buildFileContextMenu()));
+
+ // Connect
+ connect(m_projectExplorer, SIGNAL(currentNodeChanged(ProjectExplorer::Node*,ProjectExplorer::Project*)),
+ this, SLOT(updateContextActions(ProjectExplorer::Node*,ProjectExplorer::Project*)));
+
+ connect(m_projectExplorer->buildManager(), SIGNAL(buildStateChanged(ProjectExplorer::Project*)),
+ this, SLOT(buildStateChanged(ProjectExplorer::Project*)));
+
+ // Run initial setup routines
+ updateContextActions(0, 0);
+ updateReparseQbsAction();
+
+ return true;
+}
+
+void QbsProjectManagerPlugin::extensionsInitialized()
+{ }
+
+void QbsProjectManagerPlugin::updateContextActions(ProjectExplorer::Node *node, ProjectExplorer::Project *project)
+{
+ if (m_currentProject) {
+ disconnect(m_currentProject, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
+ this, SLOT(activeTargetChanged()));
+ disconnect(m_currentProject, SIGNAL(projectParsingStarted()),
+ this, SLOT(parsingStateChanged()));
+ disconnect(m_currentProject, SIGNAL(projectParsingDone(bool)),
+ this, SLOT(parsingStateChanged()));
+ }
+
+ m_currentNode = node;
+ m_currentProject = qobject_cast<Internal::QbsProject *>(project);
+ if (m_currentProject) {
+ connect(m_currentProject, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
+ this, SLOT(activeTargetChanged()));
+ connect(m_currentProject, SIGNAL(projectParsingStarted()),
+ this, SLOT(parsingStateChanged()));
+ connect(m_currentProject, SIGNAL(projectParsingDone(bool)),
+ this, SLOT(parsingStateChanged()));
+ }
+
+ activeTargetChanged();
+
+ bool isBuilding = m_projectExplorer->buildManager()->isBuilding(project);
+ bool isFile = m_currentProject && node && (node->nodeType() == ProjectExplorer::FileNodeType);
+ bool isFileEnabled = isFile && node->isEnabled();
+
+ m_reparseQbsCtx->setEnabled(!isBuilding && m_currentProject && !m_currentProject->isParsing());
+ m_buildFileContextMenu->setEnabled(isFileEnabled);
+}
+
+void QbsProjectManagerPlugin::updateReparseQbsAction()
+{
+ m_reparseQbs->setEnabled(m_currentProject
+ && !m_projectExplorer->buildManager()->isBuilding(m_currentProject)
+ && !m_currentProject->isParsing());
+}
+
+void QbsProjectManagerPlugin::activeTargetChanged()
+{
+ if (m_currentTarget)
+ disconnect(m_currentTarget, SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)),
+ this, SLOT(updateReparseQbsAction()));
+
+ m_currentTarget = m_currentProject ? m_currentProject->activeTarget() : 0;
+
+ if (m_currentTarget)
+ connect(m_currentTarget, SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)),
+ this, SLOT(updateReparseQbsAction()));
+
+ updateReparseQbsAction();
+}
+
+void QbsProjectManagerPlugin::buildStateChanged(ProjectExplorer::Project *project)
+{
+ if (project == m_currentProject) {
+ updateReparseQbsAction();
+ updateContextActions(m_currentNode, m_currentProject);
+ }
+}
+
+void QbsProjectManagerPlugin::parsingStateChanged()
+{
+ if (m_currentProject) {
+ updateReparseQbsAction();
+ updateContextActions(m_currentNode, m_currentProject);
+ }
+}
+
+void QbsProjectManagerPlugin::buildFileContextMenu()
+{
+ // <debug>
+ qDebug() << "Build file...";
+ // </debug>
+ QTC_ASSERT(m_currentNode, return);
+ QTC_ASSERT(m_currentProject, return);
+
+ ProjectExplorer::Target *t = m_currentProject->activeTarget();
+ if (!t)
+ return;
+ QbsBuildConfiguration *bc = qobject_cast<QbsBuildConfiguration *>(t->activeBuildConfiguration());
+ if (!bc)
+ return;
+
+ ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance();
+
+ if (!pe->saveModifiedFiles())
+ return;
+
+ bc->setChangedFiles(QStringList(m_currentNode->path()));
+
+ const Core::Id buildStep = Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD);
+
+ const QString name = ProjectExplorer::ProjectExplorerPlugin::displayNameForStepId(buildStep);
+ pe->buildManager()->buildList(bc->stepList(buildStep), name);
+
+ bc->setChangedFiles(QStringList());
+}
+
+void QbsProjectManagerPlugin::reparseCurrentProject()
+{
+ if (m_currentProject)
+ m_currentProject->parseCurrentBuildConfiguration();
+}
+
+} // namespace Internal
+} // namespace QbsProjectManager
+
+Q_EXPORT_PLUGIN(QbsProjectManager::Internal::QbsProjectManagerPlugin)
diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.h b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.h
new file mode 100644
index 0000000000..3455b8f5dc
--- /dev/null
+++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** 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 QBSPROJECTPLUGIN_H
+#define QBSPROJECTPLUGIN_H
+
+#include <extensionsystem/iplugin.h>
+
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+QT_END_NAMESPACE
+
+namespace ProjectExplorer {
+class Project;
+class ProjectExplorerPlugin;
+class Node;
+class Target;
+} // namespace ProjectExplorer
+
+namespace QbsProjectManager {
+class QbsManager;
+
+namespace Internal {
+
+class QbsProject;
+
+class QbsProjectManagerPlugin : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QbsProjectManager.json")
+
+public:
+ QbsProjectManagerPlugin();
+
+ bool initialize(const QStringList &arguments, QString *errorMessage);
+
+ void extensionsInitialized();
+
+private slots:
+ void updateContextActions(ProjectExplorer::Node *node, ProjectExplorer::Project *project);
+ void updateReparseQbsAction();
+ void activeTargetChanged();
+ void buildStateChanged(ProjectExplorer::Project *project);
+ void parsingStateChanged();
+ void buildFileContextMenu();
+
+ void reparseCurrentProject();
+
+private:
+ QbsManager *m_manager;
+ ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer;
+
+ QAction *m_reparseQbs;
+ QAction *m_reparseQbsCtx;
+ QAction *m_buildFileContextMenu;
+
+ Internal::QbsProject *m_currentProject;
+ ProjectExplorer::Target *m_currentTarget;
+ ProjectExplorer::Node *m_currentNode;
+};
+
+} // namespace Internal
+} // namespace QbsProject
+
+#endif // QBSPROJECTPLUGIN_H