Add Boost.Build Project Manager plug-in

Preparing to contribute the plug-in, see the announcement at
http://lists.qt-project.org/pipermail/qt-creator/2015-February/004436.html

Change-Id: Ic3920c9b888af5bea1b7742b8ff49984c29a2909
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: Eike Ziller <eike.ziller@theqtcompany.com>
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@theqtcompany.com>
Reviewed-by: hjk <hjk@theqtcompany.com>
This commit is contained in:
Mateusz Loskot
2015-05-06 00:16:58 +02:00
committed by Orgad Shaneh
parent 188d4bfdb2
commit 7389250fcd
40 changed files with 6051 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
{
\"Name\" : \"BoostBuildProjectManager\",
\"Version\" : \"$$QTCREATOR_VERSION\",
\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
\"Vendor\" : \"Mateusz Łoskot\",
\"Copyright\" : \"2013 (C) Mateusz Łoskot\",
\"License\" : [ \"GNU Lesser General Public License, Version 2.1\",
\"\",
\"This is free software; you can redistribute and/or modify it under the terms\",
\"of the GNU Lesser General Public License, Version 2.1 as published\",
\"by the Free Software Foundation.\",
\"See accompanying fike LICENSE.txt file or\",
\"copy at http://www.gnu.org/licenses/lgpl-2.1-standalone.html for more information.\",
\"\" ],
\"Category\" : \"Build Systems\",
\"Description\" : \"Qt Creator plugin for Boost.Build\",
\"Url\" : \"https://github.com/mloskot/qt-creator-plugin-boostbuild/\",
$$dependencyList
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<!--
Note the use of character groups, i.e. [Jj]amfile, seems to be impossible.
Also, Qt Creator at the moment disobeys this rule specified by freedesktop.org:
"Applications MUST match globs case-insensitively,
except when the case-sensitive attribute is set to true."
as per https://codereview.qt-project.org/#change,72567
-->
<mime-type type="text/x-boostbuild-project">
<sub-class-of type="text/plain"/>
<comment>Boost.Build Project file</comment>
<glob pattern="Jamroot"/>
<glob pattern="jamroot"/>
<glob pattern="Jamroot.jam"/>
<glob pattern="jamroot.jam"/>
<glob pattern="Jamfile"/>
<glob pattern="jamfile"/>
<glob pattern="Jamfile.v2"/>
<glob pattern="jamfile.v2"/>
<glob pattern="Jamfile.jam"/>
<glob pattern="jamfile.jam"/>
</mime-type>
<mime-type type="application/vnd.qtcreator.boostbuild.files">
<sub-class-of type="text/plain"/>
<comment>Boost.Build Project Files</comment>
<glob pattern="Jam*.qtcreator.files"/>
<glob pattern="jam*.qtcreator.files"/>
</mime-type>
<mime-type type="application/vnd.qtcreator.boostbuild.includes">
<sub-class-of type="text/plain"/>
<comment>Boost.Build Project Includes</comment>
<glob pattern="Jam*.qtcreator.includes"/>
<glob pattern="jam*.qtcreator.includes"/>
</mime-type>
</mime-info>

View File

@@ -0,0 +1,391 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#include "b2buildconfiguration.h"
#include "b2buildinfo.h"
#include "b2buildstep.h"
#include "b2project.h"
#include "b2projectmanagerconstants.h"
#include "b2utility.h"
#include <coreplugin/icore.h>
#include <projectexplorer/buildinfo.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/namedwidget.h>
#include <projectexplorer/target.h>
#include <utils/pathchooser.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <utils/mimetypes/mimedatabase.h>
#include <QFileInfo>
#include <QFormLayout>
#include <QInputDialog>
#include <QScopedPointer>
#include <QString>
#include <memory>
using namespace ProjectExplorer;
namespace BoostBuildProjectManager {
namespace Internal {
BuildConfiguration::BuildConfiguration(Target* parent)
: ProjectExplorer::BuildConfiguration(parent, Core::Id(Constants::BUILDCONFIGURATION_ID))
{
ProjectExplorer::Project const* p = parent->project();
Q_ASSERT(p);
setWorkingDirectory(p->projectDirectory());
}
BuildConfiguration::BuildConfiguration(Target* parent, BuildConfiguration* source)
: ProjectExplorer::BuildConfiguration(parent, source)
{
if (BuildConfiguration* bc = qobject_cast<BuildConfiguration*>(source))
setWorkingDirectory(bc->workingDirectory());
}
BuildConfiguration::BuildConfiguration(Target* parent, Core::Id const id)
: ProjectExplorer::BuildConfiguration(parent, id)
{
ProjectExplorer::Project const* p = parent->project();
Q_ASSERT(p);
setWorkingDirectory(p->projectDirectory());
}
QVariantMap BuildConfiguration::toMap() const
{
QVariantMap map(ProjectExplorer::BuildConfiguration::toMap());
map.insert(QLatin1String(Constants::BC_KEY_WORKDIR), workingDirectory_.toString());
return map;
}
bool BuildConfiguration::fromMap(QVariantMap const& map)
{
if (!ProjectExplorer::BuildConfiguration::fromMap(map))
return false;
QString dir = map.value(QLatin1String(Constants::BC_KEY_WORKDIR)).toString();
setWorkingDirectory(Utils::FileName::fromString(dir));
return true;
}
ProjectExplorer::NamedWidget*
BuildConfiguration::createConfigWidget()
{
return new BuildSettingsWidget(this);
}
BuildConfiguration::BuildType
BuildConfiguration::buildType() const
{
BuildType type = Unknown;
ProjectExplorer::BuildStepList* buildStepList
= stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD));
Q_ASSERT(buildStepList);
foreach (ProjectExplorer::BuildStep* bs, buildStepList->steps()) {
if (BuildStep* bbStep = qobject_cast<BuildStep*>(bs)) {
type = bbStep->buildType();
break;
}
}
return type;
}
Utils::FileName BuildConfiguration::workingDirectory() const
{
Q_ASSERT(!workingDirectory_.isEmpty());
return workingDirectory_;
}
void BuildConfiguration::setWorkingDirectory(Utils::FileName const& dir)
{
if (dir.isEmpty()) {
if (Target* t = target()) {
QString const dwd
= Project::defaultWorkingDirectory(t->project()->projectDirectory().toString());
workingDirectory_ = Utils::FileName::fromString(dwd);
}
} else {
workingDirectory_ = dir;
}
Q_ASSERT(!workingDirectory_.isEmpty());
emitWorkingDirectoryChanged();
}
void BuildConfiguration::emitWorkingDirectoryChanged()
{
if (workingDirectory() != lastEmmitedWorkingDirectory_) {
lastEmmitedWorkingDirectory_= workingDirectory();
emit workingDirectoryChanged();
}
}
BuildConfigurationFactory::BuildConfigurationFactory(QObject* parent)
: IBuildConfigurationFactory(parent)
{
}
int
BuildConfigurationFactory::priority(ProjectExplorer::Target const* parent) const
{
return canHandle(parent) ? 0 : -1;
}
int
BuildConfigurationFactory::priority(ProjectExplorer::Kit const* k
, QString const& projectPath) const
{
BBPM_QDEBUG(k->displayName() << ", " << projectPath);
Utils::MimeDatabase mdb;
Utils::MimeType mimeType = mdb.mimeTypeForFile(QFileInfo(projectPath));
return (k && mimeType.matchesName(QLatin1String(Constants::MIMETYPE_JAMFILE)))
? 0
: -1;
}
QList<ProjectExplorer::BuildInfo*>
BuildConfigurationFactory::availableBuilds(ProjectExplorer::Target const* parent) const
{
BBPM_QDEBUG("target: " << parent->displayName());
ProjectExplorer::Project* project = parent->project();
QString const projectPath(project->projectDirectory().toString());
BBPM_QDEBUG(projectPath);
QList<ProjectExplorer::BuildInfo*> result;
result << createBuildInfo(parent->kit(), projectPath, BuildConfiguration::Debug);
result << createBuildInfo(parent->kit(), projectPath, BuildConfiguration::Release);
return result;
}
QList<ProjectExplorer::BuildInfo*>
BuildConfigurationFactory::availableSetups(ProjectExplorer::Kit const* k
, QString const& projectPath) const
{
BBPM_QDEBUG(projectPath);
QList<ProjectExplorer::BuildInfo*> result;
result << createBuildInfo(k, projectPath, BuildConfiguration::Debug);
result << createBuildInfo(k, projectPath, BuildConfiguration::Release);
return result;
}
ProjectExplorer::BuildConfiguration*
BuildConfigurationFactory::create(ProjectExplorer::Target* parent
, ProjectExplorer::BuildInfo const* info) const
{
QTC_ASSERT(parent, return 0);
QTC_ASSERT(info->factory() == this, return 0);
QTC_ASSERT(info->kitId == parent->kit()->id(), return 0);
QTC_ASSERT(!info->displayName.isEmpty(), return 0);
BBPM_QDEBUG(info->displayName);
Project* project = qobject_cast<Project*>(parent->project());
QTC_ASSERT(project, return 0);
BuildInfo const* bi = static_cast<BuildInfo const*>(info);
QScopedPointer<BuildConfiguration> bc(new BuildConfiguration(parent));
bc->setDisplayName(bi->displayName);
bc->setDefaultDisplayName(bi->displayName);
bc->setBuildDirectory(bi->buildDirectory);
bc->setWorkingDirectory(bi->workingDirectory);
BuildStepFactory* stepFactory = BuildStepFactory::getObject();
QTC_ASSERT(stepFactory, return 0);
// Build steps
if (ProjectExplorer::BuildStepList* buildSteps
= bc->stepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD)) {
BuildStep* step = stepFactory->create(buildSteps);
QTC_ASSERT(step, return 0);
step->setBuildType(bi->buildType);
buildSteps->insertStep(0, step);
}
// Clean steps
if (ProjectExplorer::BuildStepList* cleanSteps
= bc->stepList(ProjectExplorer::Constants::BUILDSTEPS_CLEAN)) {
BuildStep* step = stepFactory->create(cleanSteps);
QTC_ASSERT(step, return 0);
step->setBuildType(bi->buildType);
cleanSteps->insertStep(0, step);
}
return bc.take();
}
bool
BuildConfigurationFactory::canClone(ProjectExplorer::Target const* parent
, ProjectExplorer::BuildConfiguration* source) const
{
Q_ASSERT(parent);
Q_ASSERT(source);
return canHandle(parent)
? source->id() == Constants::BUILDCONFIGURATION_ID
: false;
}
BuildConfiguration*
BuildConfigurationFactory::clone(ProjectExplorer::Target* parent
, ProjectExplorer::BuildConfiguration* source)
{
Q_ASSERT(parent);
Q_ASSERT(source);
BuildConfiguration* copy = 0;
if (canClone(parent, source)) {
BuildConfiguration* old = static_cast<BuildConfiguration*>(source);
copy = new BuildConfiguration(parent, old);
}
return copy;
}
bool
BuildConfigurationFactory::canRestore(ProjectExplorer::Target const* parent
, QVariantMap const& map) const
{
Q_ASSERT(parent);
return canHandle(parent)
? ProjectExplorer::idFromMap(map) == Constants::BUILDCONFIGURATION_ID
: false;
}
BuildConfiguration*
BuildConfigurationFactory::restore(ProjectExplorer::Target *parent
, QVariantMap const& map)
{
Q_ASSERT(parent);
if (canRestore(parent, map)) {
QScopedPointer<BuildConfiguration> bc(new BuildConfiguration(parent));
if (bc->fromMap(map))
return bc.take();
}
return 0;
}
bool
BuildConfigurationFactory::canHandle(ProjectExplorer::Target const* t) const
{
QTC_ASSERT(t, return false);
return t->project()->supportsKit(t->kit())
? t->project()->id() == Constants::PROJECT_ID
: false;
}
BuildInfo*
BuildConfigurationFactory::createBuildInfo(ProjectExplorer::Kit const* k
, QString const& projectPath
, BuildConfiguration::BuildType type) const
{
Q_ASSERT(k);
BuildInfo* info = new BuildInfo(this);
if (type == BuildConfiguration::Release)
info->displayName = tr("Release");
else
info->displayName = tr("Debug");
info->typeName = tr("Default (%1)").arg(info->displayName);
info->buildType = type;
info->buildDirectory = defaultBuildDirectory(projectPath);
info->workingDirectory = defaultWorkingDirectory(projectPath);
info->kitId = k->id();
BBPM_QDEBUG(info->typeName << " in " << projectPath);
return info;
}
/*static*/ Utils::FileName
BuildConfigurationFactory::defaultBuildDirectory(QString const& top)
{
return Utils::FileName::fromString(Project::defaultBuildDirectory(top));
}
/*static*/ Utils::FileName
BuildConfigurationFactory::defaultWorkingDirectory(QString const& top)
{
return Utils::FileName::fromString(Project::defaultWorkingDirectory(top));
}
BuildSettingsWidget::BuildSettingsWidget(BuildConfiguration* bc)
: bc_(bc)
, buildPathChooser_(0)
{
setDisplayName(tr("Boost.Build Manager"));
QFormLayout* fl = new QFormLayout(this);
fl->setContentsMargins(0, -1, 0, -1);
fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
QString const projectPath(bc_->target()->project()->projectDirectory().toString());
// Working directory
workPathChooser_ = new Utils::PathChooser(this);
workPathChooser_->setEnabled(true);
workPathChooser_->setEnvironment(bc_->environment());
workPathChooser_->setBaseDirectory(projectPath);
workPathChooser_->setPath(bc_->workingDirectory().toString());
fl->addRow(tr("Run Boost.Build in:"), workPathChooser_);
// Build directory
buildPathChooser_ = new Utils::PathChooser(this);
buildPathChooser_->setEnabled(true);
buildPathChooser_->setEnvironment(bc_->environment());
buildPathChooser_->setBaseDirectory(projectPath);
buildPathChooser_->setPath(bc_->rawBuildDirectory().toString());
fl->addRow(tr("Set build directory to:"), buildPathChooser_);
connect(workPathChooser_, SIGNAL(changed(QString))
, this, SLOT(workingDirectoryChanged()));
connect(buildPathChooser_, SIGNAL(changed(QString))
, this, SLOT(buildDirectoryChanged()));
connect(bc, SIGNAL(environmentChanged())
, this, SLOT(environmentHasChanged()));
}
void BuildSettingsWidget::buildDirectoryChanged()
{
QTC_ASSERT(bc_, return);
bc_->setBuildDirectory(Utils::FileName::fromString(buildPathChooser_->rawPath()));
}
void BuildSettingsWidget::workingDirectoryChanged()
{
QTC_ASSERT(bc_, return);
bc_->setWorkingDirectory(Utils::FileName::fromString(workPathChooser_->rawPath()));
}
void BuildSettingsWidget::environmentHasChanged()
{
Q_ASSERT(buildPathChooser_);
buildPathChooser_->setEnvironment(bc_->environment());
}
} // namespace Internal
} // namespace BoostBuildProjectManager

View File

@@ -0,0 +1,138 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBBUILDCONFIGURATION_HPP
#define BBBUILDCONFIGURATION_HPP
// Qt Creator
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/namedwidget.h>
#include <utils/fileutils.h>
// Qt
#include <QList>
#include <QString>
#include <QVariantMap>
namespace Utils {
class FileName;
class PathChooser;
}
namespace BoostBuildProjectManager {
namespace Internal {
class BuildInfo;
class BuildSettingsWidget;
class BuildConfiguration : public ProjectExplorer::BuildConfiguration
{
Q_OBJECT
friend class BuildConfigurationFactory;
public:
explicit BuildConfiguration(ProjectExplorer::Target* parent);
QVariantMap toMap() const;
ProjectExplorer::NamedWidget* createConfigWidget();
BuildType buildType() const;
Utils::FileName workingDirectory() const;
void setWorkingDirectory(Utils::FileName const& dir);
signals:
void workingDirectoryChanged();
protected:
BuildConfiguration(ProjectExplorer::Target* parent, BuildConfiguration* source);
BuildConfiguration(ProjectExplorer::Target* parent, Core::Id const id);
bool fromMap(QVariantMap const& map);
friend class BuildSettingsWidget;
private slots:
void emitWorkingDirectoryChanged();
private:
Utils::FileName workingDirectory_;
Utils::FileName lastEmmitedWorkingDirectory_;
};
class BuildConfigurationFactory : public ProjectExplorer::IBuildConfigurationFactory
{
Q_OBJECT
public:
explicit BuildConfigurationFactory(QObject* parent = 0);
int priority(ProjectExplorer::Target const* parent) const;
int priority(ProjectExplorer::Kit const* k, QString const& projectPath) const;
QList<ProjectExplorer::BuildInfo*>
availableBuilds(ProjectExplorer::Target const* parent) const;
QList<ProjectExplorer::BuildInfo*>
availableSetups(ProjectExplorer::Kit const* k, QString const& projectPath) const;
ProjectExplorer::BuildConfiguration*
create(ProjectExplorer::Target* parent
, ProjectExplorer::BuildInfo const* info) const;
bool
canClone(ProjectExplorer::Target const* parent
, ProjectExplorer::BuildConfiguration* source) const;
BuildConfiguration*
clone(ProjectExplorer::Target* parent, ProjectExplorer::BuildConfiguration* source);
bool
canRestore(ProjectExplorer::Target const* parent, QVariantMap const& map) const;
BuildConfiguration*
restore(ProjectExplorer::Target *parent, QVariantMap const& map);
static Utils::FileName defaultBuildDirectory(QString const& top);
static Utils::FileName defaultWorkingDirectory(QString const& top);
private:
bool canHandle(ProjectExplorer::Target const* target) const;
BuildInfo*
createBuildInfo(ProjectExplorer::Kit const* k
, QString const& projectPath
, BuildConfiguration::BuildType type) const;
};
class BuildSettingsWidget : public ProjectExplorer::NamedWidget
{
Q_OBJECT
public:
BuildSettingsWidget(BuildConfiguration* bc);
private slots:
void environmentHasChanged();
void buildDirectoryChanged();
void workingDirectoryChanged();
private:
BuildConfiguration* bc_;
Utils::PathChooser* workPathChooser_;
Utils::PathChooser* buildPathChooser_;
};
} // namespace Internal
} // namespace BoostBuildProjectManager
#endif // BBBUILDCONFIGURATION_HPP

View File

@@ -0,0 +1,26 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#include "b2buildinfo.h"
#include "b2buildconfiguration.h"
#include "b2projectmanagerconstants.h"
namespace BoostBuildProjectManager {
namespace Internal {
BuildInfo::BuildInfo(BuildConfigurationFactory const* f)
: ProjectExplorer::BuildInfo(f)
, buildType(BuildConfiguration::Debug)
{
}
} // namespace Internal
} // namespace BoostBuildProjectManager

View File

@@ -0,0 +1,44 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBBUILDINFO_HPP
#define BBBUILDINFO_HPP
#include "b2buildconfiguration.h"
// Qt Creator
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildinfo.h>
#include <utils/fileutils.h>
// Qt
namespace BoostBuildProjectManager {
namespace Internal {
class BuildConfigurationFactory;
class BuildInfo : public ProjectExplorer::BuildInfo
{
public:
explicit BuildInfo(BuildConfigurationFactory const* f);
// Boost.Build option variant={debug|release}
// By default, the debug variant is set.
BuildConfiguration::BuildType buildType;
// Boost.Build command working directory.
// By default, empty what indicates project path from where Jamfile was opened.
Utils::FileName workingDirectory;
};
} // namespace Internal
} // namespace BoostBuildProjectManager
#endif // BBBUILDINFO_HPP

View File

@@ -0,0 +1,420 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
// Copyright (C) 2013 Openismus GmbH.
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#include "b2buildconfiguration.h"
#include "b2buildstep.h"
#include "b2outputparser.h"
#include "b2projectmanagerconstants.h"
#include "b2utility.h"
// Qt Creator
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildstep.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/processparameters.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectconfiguration.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
#include <utils/environment.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
// Qt
#include <QFormLayout>
#include <QLineEdit>
#include <QScopedPointer>
#include <QString>
// std
#include <memory>
namespace BoostBuildProjectManager {
namespace Internal {
BuildStep::BuildStep(ProjectExplorer::BuildStepList* bsl)
: ProjectExplorer::AbstractProcessStep(bsl, Core::Id(Constants::BUILDSTEP_ID))
{
}
BuildStep::BuildStep(ProjectExplorer::BuildStepList* bsl, BuildStep* bs)
: AbstractProcessStep(bsl, bs)
, tasks_(bs->tasks_)
, arguments_(bs->arguments_)
{
}
BuildStep::BuildStep(ProjectExplorer::BuildStepList* bsl, Core::Id const id)
: AbstractProcessStep(bsl, id)
{
}
bool BuildStep::init()
{
setDefaultDisplayName(QLatin1String(Constants::BOOSTBUILD));
tasks_.clear();
ProjectExplorer::ToolChain* tc
= ProjectExplorer::ToolChainKitInformation::toolChain(target()->kit());
if (!tc) {
BBPM_QDEBUG("Qt Creator needs compiler");
typedef ProjectExplorer::Task Task;
Task task(Task::Error
, tr("Qt Creator needs a compiler set up to build. Configure a compiler in the kit options.")
, Utils::FileName(), -1
, ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
tasks_.append(task);
return !tasks_.empty(); // otherwise the tasks will not get reported
}
BuildConfiguration* bc = thisBuildConfiguration();
QTC_ASSERT(bc, return false);
setIgnoreReturnValue(Constants::ReturnValueNotIgnored);
ProjectExplorer::ProcessParameters* pp = processParameters();
pp->setMacroExpander(bc->macroExpander());
{
// from GenericProjectManager code:
// Force output to english for the parsers.
// Do this here and not in the toolchain's addToEnvironment() to not
// screw up the users run environment.
Utils::Environment env = bc->environment();
env.set(QLatin1String("LC_ALL"), QLatin1String("C"));
pp->setEnvironment(env);
}
pp->setWorkingDirectory(bc->workingDirectory().toString());
pp->setCommand(makeCommand(bc->environment()));
pp->setArguments(allArguments());
pp->resolveAll();
// Create Boost.Build parser and chain with existing parsers
setOutputParser(new BoostBuildParser());
if (ProjectExplorer::IOutputParser* parser = target()->kit()->createOutputParser())
appendOutputParser(parser);
outputParser()->setWorkingDirectory(pp->effectiveWorkingDirectory());
BBPM_QDEBUG(displayName() << ", " << bc->buildDirectory().toString()
<< ", " << pp->effectiveWorkingDirectory());
return ProjectExplorer::AbstractProcessStep::init();
}
void BuildStep::run(QFutureInterface<bool>& fi)
{
BBPM_QDEBUG("running: " << displayName());
bool canContinue = true;
foreach (ProjectExplorer::Task const& t, tasks_) {
addTask(t);
canContinue = false;
}
if (!canContinue) {
emit addOutput(tr("Configuration is faulty. Check the Issues view for details.")
, BuildStep::MessageOutput);
fi.reportResult(false);
emit finished();
} else {
AbstractProcessStep::run(fi);
}
}
BuildConfiguration* BuildStep::thisBuildConfiguration() const
{
BuildConfiguration* bc = 0;
if (ProjectExplorer::BuildConfiguration* bcBase = buildConfiguration()) {
bc = qobject_cast<BuildConfiguration*>(bcBase);
} else {
// TODO: Do we need to do anything with this case?
// From QmakeProjectManager:
// That means the step is in the deploylist, so we listen to the active build
// config changed signal and react...
bcBase = target()->activeBuildConfiguration();
bc = qobject_cast<BuildConfiguration*>(bcBase);
}
return bc;
}
ProjectExplorer::BuildStepConfigWidget* BuildStep::createConfigWidget()
{
return new BuildStepConfigWidget(this);
}
bool BuildStep::immutable() const
{
return false;
}
QVariantMap BuildStep::toMap() const
{
QVariantMap map(ProjectExplorer::AbstractProcessStep::toMap());
map.insert(QLatin1String(Constants::BS_KEY_ARGUMENTS), additionalArguments());
return map;
}
bool BuildStep::fromMap(QVariantMap const& map)
{
QString const args(map.value(QLatin1String(Constants::BS_KEY_ARGUMENTS)).toString());
setAdditionalArguments(args);
return ProjectExplorer::AbstractProcessStep::fromMap(map);
}
QString BuildStep::makeCommand(Utils::Environment const& env) const
{
Q_UNUSED(env);
return QLatin1String(Constants::COMMAND_BB2);
}
QString BuildStep::additionalArguments() const
{
return Utils::QtcProcess::joinArgs(arguments_);
}
QString BuildStep::allArguments() const
{
QStringList args(arguments_);
// Collect implicit arguments not specified by user directly as option=value pair
if (ProjectExplorer::BuildConfiguration* bc = buildConfiguration()) {
QString const builddir(bc->buildDirectory().toString());
if (!builddir.isEmpty())
args.append(QLatin1String("--build-dir=") + builddir);
}
return Utils::QtcProcess::joinArgs(args);
}
void BuildStep::appendAdditionalArgument(QString const& arg)
{
// TODO: use map or find duplicates?
arguments_.append(arg);
}
void BuildStep::setAdditionalArguments(QString const& args)
{
Utils::QtcProcess::SplitError err;
QStringList argsList = Utils::QtcProcess::splitArgs(args, Utils::HostOsInfo::hostOs(), false, &err);
if (err == Utils::QtcProcess::SplitOk) {
arguments_ = argsList;
emit argumentsChanged(args);
}
}
ProjectExplorer::BuildConfiguration::BuildType
BuildStep::buildType() const
{
// TODO: what is user inputs "variant = release" or mixed-case value?
return arguments_.contains(QLatin1String("variant=release"))
? BuildConfiguration::Release
: BuildConfiguration::Debug;
}
void
BuildStep::setBuildType(ProjectExplorer::BuildConfiguration::BuildType type)
{
// TODO: Move literals to constants
QString arg(QLatin1String("variant="));
if (type == BuildConfiguration::Release)
arg += QLatin1String("release");
else
arg += QLatin1String("debug");
appendAdditionalArgument(arg);
}
BuildStepFactory::BuildStepFactory(QObject* parent)
: IBuildStepFactory(parent)
{
}
/*static*/ BuildStepFactory* BuildStepFactory::getObject()
{
return ExtensionSystem::PluginManager::getObject<BuildStepFactory>();
}
QList<Core::Id>
BuildStepFactory::availableCreationIds(ProjectExplorer::BuildStepList* parent) const
{
return canHandle(parent)
? QList<Core::Id>() << Core::Id(Constants::BUILDSTEP_ID)
: QList<Core::Id>();
}
QString BuildStepFactory::displayNameForId(Core::Id const id) const
{
BBPM_QDEBUG("id: " << id.toString());
QString name;
if (id == Constants::BUILDSTEP_ID) {
name = tr("Boost.Build"
, "Display name for BoostBuildProjectManager::BuildStep id.");
}
return name;
}
bool BuildStepFactory::canCreate(ProjectExplorer::BuildStepList* parent
, Core::Id const id) const
{
return canHandle(parent) && Core::Id(Constants::BUILDSTEP_ID) == id;
}
BuildStep*
BuildStepFactory::create(ProjectExplorer::BuildStepList* parent)
{
ProjectExplorer::BuildStep* step = create(parent, Constants::BUILDSTEP_ID);
return qobject_cast<BuildStep*>(step);
}
ProjectExplorer::BuildStep*
BuildStepFactory::create(ProjectExplorer::BuildStepList* parent, Core::Id const id)
{
BBPM_QDEBUG("id: " << id.toString());
if (!canCreate(parent, id))
return 0;
BuildStep* step = new BuildStep(parent);
if (parent->id() == ProjectExplorer::Constants::BUILDSTEPS_CLEAN)
step->appendAdditionalArgument(QLatin1String("--clean"));
return step;
}
bool BuildStepFactory::canClone(ProjectExplorer::BuildStepList *parent
, ProjectExplorer::BuildStep *source) const
{
return canCreate(parent, source->id());
}
ProjectExplorer::BuildStep*
BuildStepFactory::clone(ProjectExplorer::BuildStepList* parent
, ProjectExplorer::BuildStep* source)
{
return canClone(parent, source)
? new BuildStep(parent, static_cast<BuildStep*>(source))
: 0;
}
bool BuildStepFactory::canRestore(ProjectExplorer::BuildStepList* parent
, QVariantMap const& map) const
{
return canCreate(parent, ProjectExplorer::idFromMap(map));
}
ProjectExplorer::BuildStep*
BuildStepFactory::restore(ProjectExplorer::BuildStepList* parent
, QVariantMap const& map)
{
Q_ASSERT(parent);
if (canRestore(parent, map)) {
QScopedPointer<BuildStep> bs(new BuildStep(parent));
if (bs->fromMap(map))
return bs.take();
}
return 0;
}
bool BuildStepFactory::canHandle(ProjectExplorer::BuildStepList* parent) const
{
QTC_ASSERT(parent, return false);
return parent->target()->project()->id() == Constants::PROJECT_ID;
}
BuildStepConfigWidget::BuildStepConfigWidget(BuildStep* step)
: step_(step)
{
QFormLayout *fl = new QFormLayout(this);
fl->setMargin(0);
fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
setLayout(fl);
arguments_ = new QLineEdit(this);
arguments_->setText(step_->additionalArguments());
fl->addRow(tr("Arguments:"), arguments_);
updateDetails();
connect(arguments_, SIGNAL(textChanged(QString))
, step, SLOT(setAdditionalArguments(QString)));
connect(step, SIGNAL(argumentsChanged(QString))
, this, SLOT(updateDetails()));
connect(step_->project(), SIGNAL(environmentChanged())
, this, SLOT(updateDetails()));
if (BuildConfiguration* bc = step_->thisBuildConfiguration()) {
connect(bc, SIGNAL(buildDirectoryChanged())
, this, SLOT(updateDetails()));
}
}
BuildStepConfigWidget::~BuildStepConfigWidget()
{
}
QString BuildStepConfigWidget::displayName() const
{
return tr(Constants::BOOSTBUILD
, "BoostBuildProjectManager::BuildStep display name.");
}
QString BuildStepConfigWidget::summaryText() const
{
return summaryText_;
}
void BuildStepConfigWidget::setSummaryText(QString const& text)
{
if (text != summaryText_) {
summaryText_ = text;
emit updateSummary();
}
}
void BuildStepConfigWidget::updateDetails()
{
ProjectExplorer::ToolChain* tc
= ProjectExplorer::ToolChainKitInformation::toolChain(step_->target()->kit());
if (!tc) {
setSummaryText(tr("<b>%1:</b> %2")
.arg(QLatin1String(Constants::BOOSTBUILD))
.arg(ProjectExplorer::ToolChainKitInformation::msgNoToolChainInTarget()));
return;
}
BuildConfiguration* bc = step_->thisBuildConfiguration();
QTC_ASSERT(bc, return;);
ProjectExplorer::ProcessParameters params;
params.setMacroExpander(bc->macroExpander());
params.setEnvironment(bc->environment());
params.setWorkingDirectory(bc->workingDirectory().toString());
params.setCommand(step_->makeCommand(bc->environment()));
params.setArguments(step_->allArguments());
if (params.commandMissing()) {
setSummaryText(tr("<b>%1:</b> %2 not found in the environment.")
.arg(QLatin1String(Constants::BOOSTBUILD))
.arg(params.command())); // Override display text
} else {
setSummaryText(params.summary(displayName()));
}
}
} // namespace Internal
} // namespace BoostBuildProjectManager

View File

@@ -0,0 +1,162 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBBUILDSTEP_HPP
#define BBBUILDSTEP_HPP
// Qt Creator
#include <projectexplorer/abstractprocessstep.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildstep.h>
#include <projectexplorer/task.h>
// Qt
#include <QLineEdit>
#include <QList>
#include <QString>
#include <QVariantMap>
namespace ProjectExplorer {
class Project;
}
namespace Utils {
class Environment;
}
namespace BoostBuildProjectManager {
namespace Internal {
class BuildConfiguration;
class BuildStep : public ProjectExplorer::AbstractProcessStep
{
Q_OBJECT
friend class BuildStepFactory;
friend class BuildStepConfigWidget;
public:
explicit BuildStep(ProjectExplorer::BuildStepList* bsl);
bool init();
void run(QFutureInterface<bool>& interface);
ProjectExplorer::BuildStepConfigWidget* createConfigWidget();
bool immutable() const;
QVariantMap toMap() const;
bool fromMap(QVariantMap const& map);
QString makeCommand(Utils::Environment const& env) const;
QString additionalArguments() const;
QString allArguments() const;
void appendAdditionalArgument(QString const& arg);
ProjectExplorer::BuildConfiguration::BuildType
buildType() const;
void setBuildType(ProjectExplorer::BuildConfiguration::BuildType type);
public slots:
void setAdditionalArguments(QString const& list);
signals:
void argumentsChanged(QString const& list);
protected:
BuildStep(ProjectExplorer::BuildStepList* bsl, BuildStep* bs);
BuildStep(ProjectExplorer::BuildStepList* bsl, Core::Id const id);
private:
BuildConfiguration* thisBuildConfiguration() const;
QList<ProjectExplorer::Task> tasks_;
QStringList arguments_;
};
// Factory used to create instances of BuildStep.
class BuildStepFactory : public ProjectExplorer::IBuildStepFactory
{
Q_OBJECT
public:
BuildStepFactory(QObject* parent = 0);
static BuildStepFactory* getObject();
QList<Core::Id>
availableCreationIds(ProjectExplorer::BuildStepList* bc) const;
QString
displayNameForId(const Core::Id id) const;
bool
canCreate(ProjectExplorer::BuildStepList* parent, Core::Id const id) const;
BuildStep*
create(ProjectExplorer::BuildStepList* parent);
ProjectExplorer::BuildStep*
create(ProjectExplorer::BuildStepList* parent, Core::Id const id);
bool
canClone(ProjectExplorer::BuildStepList *parent
, ProjectExplorer::BuildStep *source) const;
ProjectExplorer::BuildStep*
clone(ProjectExplorer::BuildStepList* parent, ProjectExplorer::BuildStep* source);
bool
canRestore(ProjectExplorer::BuildStepList* parent, QVariantMap const& map) const;
ProjectExplorer::BuildStep*
restore(ProjectExplorer::BuildStepList* parent, QVariantMap const& map);
private:
bool
canHandle(ProjectExplorer::BuildStepList* parent) const;
};
class BuildStepConfigWidget : public ProjectExplorer::BuildStepConfigWidget
{
Q_OBJECT
public:
explicit BuildStepConfigWidget(BuildStep* buildStep);
~BuildStepConfigWidget();
QString displayName() const;
QString summaryText() const;
private slots:
void updateDetails();
private:
void setSummaryText(const QString &text);
ProjectExplorer::BuildConfiguration* bc_;
BuildStep* step_;
QString summaryText_;
QLineEdit* arguments_;
};
} // namespace Internal
} // namespace BoostBuildProjectManager
#endif // BBBUILDSTEP_HPP

View File

@@ -0,0 +1,272 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#include "b2openprojectwizard.h"
#include "b2project.h"
#include "b2projectmanagerconstants.h"
#include "b2utility.h"
#include "filesselectionwizardpage.h"
// Qt Creator
#include <coreplugin/iwizardfactory.h>
#include <coreplugin/icore.h>
#include <projectexplorer/customwizard/customwizard.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
#include <utils/mimetypes/mimedatabase.h>
// Qt
#include <QFileInfo>
#include <QFormLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
namespace BoostBuildProjectManager {
namespace Internal {
//////////////////////////////////////////////////////////////////////////////////////////
OpenProjectWizard::OpenProjectWizard(Project const* const project)
: project_(project)
, projectOpened_(false)
{
// Project instance has been created, but it's only partially initialised and
// rest of the initialization takes place after this wizard completes.
Q_ASSERT(project_);
setDisplayName(tr("Open %1 Project").arg(BBPM_C(BOOSTBUILD)));
setId(BBPM_C(PROJECT_WIZARD_ID));
setWizardKind(ProjectWizard); // affects dir vs file path and sub-projects handling
// TODO: do we need categories or flags?
}
bool OpenProjectWizard::run(QString const& platform, QVariantMap const& extraValues)
{
QVariantMap extraValuesCopy(extraValues);
// Project name should be passed by caller, but,
// since we have Project instance handy, double-check.
if (!extraValuesCopy.contains(BBPM_C(P_KEY_PROJECTNAME)))
extraValuesCopy.insert(BBPM_C(P_KEY_PROJECTNAME), project_->displayName());
projectOpened_ = false;
outputValues_.clear();
runWizard(project_->projectFilePath().toString(), 0, platform, extraValuesCopy);
return projectOpened_;
}
Core::BaseFileWizard *OpenProjectWizard::create(QWidget* parent, Core::WizardDialogParameters const& parameters) const
{
OpenProjectWizardDialog* wizard(new OpenProjectWizardDialog(parent
, parameters.defaultPath(), parameters.extraValues()
, const_cast<OpenProjectWizard*>(this)->outputValues_));
foreach (QWizardPage* p, parameters.extensionPages())
wizard->addPage(p);
return wizard;
}
Core::GeneratedFiles
OpenProjectWizard::generateFiles(QWizard const* wizard, QString* errorMessage) const
{
Q_UNUSED(errorMessage)
Q_ASSERT(project_);
OpenProjectWizardDialog const* openWizard
= qobject_cast<OpenProjectWizardDialog const*>(wizard);
QDir const projectDir(openWizard->path());
// Set up MIME filters for C/C++ headers
QStringList headerFilters;
QStringList const headerMimeTypes = QStringList()
<< QLatin1String("text/x-chdr") << QLatin1String("text/x-c++hdr");
Utils::MimeDatabase mdb;
foreach (QString const& headerMime, headerMimeTypes) {
Utils::MimeType mime = mdb.mimeTypeForName(headerMime);
foreach (QString const& gp, mime.globPatterns())
headerFilters.append(gp);
}
// Generate list of include paths.
// If any C/C++ headers in any directory from paths, add it to include paths,
// used for C/C++ parsing only.
QStringList includePaths;
QStringList const paths = openWizard->selectedPaths();
foreach (QString const& path, paths) {
QFileInfo const fileInfo(path);
QDir const thisDir(fileInfo.absoluteFilePath());
if (!thisDir.entryList(headerFilters, QDir::Files).isEmpty()) {
QString const relative = projectDir.relativeFilePath(thisDir.path());
includePaths.append(relative.isEmpty() ? QLatin1String(".") : relative);
}
}
// Generate list of sources
QStringList sources = openWizard->selectedFiles();
Utility::makeRelativePaths(projectDir.absolutePath(), sources);
Core::GeneratedFile generatedFilesFile(project_->filesFilePath());
generatedFilesFile.setContents(sources.join(QLatin1String("\n")));
Core::GeneratedFile generatedIncludesFile(project_->includesFilePath());
generatedIncludesFile.setContents(includePaths.join(QLatin1String("\n")));
Core::GeneratedFiles files;
files.append(generatedFilesFile);
files.append(generatedIncludesFile);
return files;
}
bool
OpenProjectWizard::postGenerateFiles(QWizard const* wizard
, Core::GeneratedFiles const& files, QString* errorMessage)
{
Q_UNUSED(wizard);
projectOpened_
= ProjectExplorer::CustomProjectWizard::postGenerateOpen(files, errorMessage);
return projectOpened_;
}
//////////////////////////////////////////////////////////////////////////////////////////
OpenProjectWizardDialog::OpenProjectWizardDialog(QWidget* parent
, QString const& projectFile
, QVariantMap const& extraValues, QVariantMap& outputValues)
: Core::BaseFileWizard(parent)
, outputValues_(outputValues)
, extraValues_(extraValues)
, projectFile_(projectFile)
{
setWindowTitle(tr("Open %1 Project").arg(BBPM_C(BOOSTBUILD)));
pathsPage_ = new PathsSelectionWizardPage(this);
pathsPage_->setTitle(tr("Project Name and Paths"));
int const pathsPageId = addPage(pathsPage_);
wizardProgress()->item(pathsPageId)->setTitle(tr("Location"));
filesPage_ = new FilesSelectionWizardPage(this);
filesPage_->setTitle(tr("File Selection"));
int const filesPageId = addPage(filesPage_);
wizardProgress()->item(filesPageId)->setTitle(tr("Files"));
}
QString OpenProjectWizardDialog::path() const
{
QFileInfo const projectFileInfo(projectFile());
QTC_ASSERT(projectFileInfo.isFile(), return QString());
return projectFileInfo.absoluteDir().absolutePath();
}
QString OpenProjectWizardDialog::projectFile() const
{
return projectFile_;
}
QString OpenProjectWizardDialog::projectName() const
{
return pathsPage_->projectName();
}
QString OpenProjectWizardDialog::defaultProjectName() const
{
return extraValues_.value(BBPM_C(P_KEY_PROJECTNAME)).toString();
}
QStringList OpenProjectWizardDialog::selectedFiles() const
{
return filesPage_->selectedFiles();
}
QStringList OpenProjectWizardDialog::selectedPaths() const
{
return filesPage_->selectedPaths();
}
void OpenProjectWizardDialog::setProjectName(QString const& name)
{
outputValues_.insert(QLatin1String(Constants::P_KEY_PROJECTNAME), name);
}
//////////////////////////////////////////////////////////////////////////////////////////
PathsSelectionWizardPage::PathsSelectionWizardPage(OpenProjectWizardDialog* wizard)
: QWizardPage(wizard)
, wizard_(wizard)
{
QFormLayout *fl = new QFormLayout();
setLayout(fl);
QLabel* pathLabel = new QLabel(this);
pathLabel->setText(tr("Opening the following Jamfile as a project:"));
fl->addRow(pathLabel);
QLineEdit* pathLine = new QLineEdit(this);
pathLine->setReadOnly(true);
pathLine->setDisabled(true);
pathLine->setText(wizard_->projectFile());
fl->addRow(pathLine);
QString projectName(Utility::parseJamfileProjectName(wizard_->projectFile()));
if (projectName.isEmpty())
projectName = wizard_->defaultProjectName();
nameLineEdit_ = new QLineEdit(this);
connect(nameLineEdit_, &QLineEdit::textChanged
, wizard_, &OpenProjectWizardDialog::setProjectName);
nameLineEdit_->setText(projectName);
fl->addRow(tr("Project name:"), nameLineEdit_);
QLabel* defaultsLabel = new QLabel(this);
defaultsLabel->setText(tr("Default Boost.Build runtime locations:"));
fl->addRow(defaultsLabel);
QLineEdit* workingLine = new QLineEdit(this);
workingLine->setReadOnly(true);
workingLine->setDisabled(true);
workingLine->setText(Project::defaultWorkingDirectory(wizard_->projectFile()));
fl->addRow(tr("Working directory:"), workingLine);
QLineEdit* buildLine = new QLineEdit(this);
buildLine->setReadOnly(true);
buildLine->setDisabled(true);
buildLine->setText(Project::defaultBuildDirectory(wizard_->projectFile()));
fl->addRow(tr("Build directory:"), buildLine);
// TODO: indicate if we can find Boost.Build executable?
QString const info(tr(
"This allows you to use Qt Creator as an IDE to edit and navigate C++ code, "
"run %1 command, work through compilation issues, "
"configure executable targets to run and debug.")
.arg(QLatin1String(Constants::BOOSTBUILD)));
QLabel* infoLabel = new QLabel(this);
infoLabel->setWordWrap(true);
infoLabel->setText(info);
fl->addRow(infoLabel);
}
QString PathsSelectionWizardPage::projectName() const
{
return nameLineEdit_->text();
}
} // namespace Internal
} // namespace BoostBuildProjectManager

View File

@@ -0,0 +1,127 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBOPENPROJECTWIZARD_HPP
#define BBOPENPROJECTWIZARD_HPP
// Qt Creator
#include <coreplugin/basefilewizard.h>
#include <coreplugin/basefilewizardfactory.h>
#include <coreplugin/generatedfile.h>
#include <utils/wizard.h>
// Qt
#include <QString>
#include <QStringList>
QT_BEGIN_NAMESPACE
class QVBoxLayout;
class QLabel;
class QTreeView;
class QLineEdit;
QT_END_NAMESPACE
namespace Utils {
class PathChooser;
}
namespace BoostBuildProjectManager {
namespace Internal {
class Project;
class PathsSelectionWizardPage;
class FilesSelectionWizardPage;
//////////////////////////////////////////////////////////////////////////////////////////
// NOTE: The Boost.Build wizard is based on Core::BaseFileWizard which seems to be
// dedicated to build "New Project" wizards. So, the plugin uses the base class in
// unconventional, matching its features to Boost.Build wizard needs, like:
// - no parent QWidget is used
// - platform name is set from default Kit display name, usually it's "Desktop"
// - extra values QVariantMap may carry custom data
// CAUTION: This wizard may stop building or start failing in run-time,
// if Qt Creator changes the base class significantly.
class OpenProjectWizard : public Core::BaseFileWizardFactory
{
Q_OBJECT
public:
OpenProjectWizard(Project const* const project);
bool run(QString const& platform, QVariantMap const& extraValues);
QVariantMap outputValues() const { return outputValues_; }
protected:
Core::BaseFileWizard*
create(QWidget* parent, Core::WizardDialogParameters const& parameters) const;
Core::GeneratedFiles
generateFiles(QWizard const* baseWizard, QString* errorMessage) const;
bool
postGenerateFiles(QWizard const* wizard
, Core::GeneratedFiles const& files, QString* errorMessage);
private:
Project const* const project_;
QVariantMap outputValues_;
bool projectOpened_;
};
//////////////////////////////////////////////////////////////////////////////////////////
class OpenProjectWizardDialog : public Core::BaseFileWizard
{
Q_OBJECT
public:
OpenProjectWizardDialog(QWidget* parent, QString const& projectFile
, QVariantMap const& extraValues, QVariantMap& outputValues);
QString path() const;
QString projectFile() const;
QString projectName() const;
QString defaultProjectName() const;
QStringList selectedFiles() const;
QStringList selectedPaths() const;
public slots:
void setProjectName(QString const& name);
private:
QVariantMap& outputValues_;
QVariantMap extraValues_;
QString projectFile_;
PathsSelectionWizardPage* pathsPage_;
FilesSelectionWizardPage* filesPage_;
};
//////////////////////////////////////////////////////////////////////////////////////////
class PathsSelectionWizardPage : public QWizardPage
{
Q_OBJECT
public:
explicit PathsSelectionWizardPage(OpenProjectWizardDialog* wizard);
QString projectName() const;
void setProjectName(QString const& name);
private:
OpenProjectWizardDialog* wizard_;
QLineEdit* nameLineEdit_;
};
} // namespace Internal
} // namespace BoostBuildProjectManager
#endif // BBOPENPROJECTWIZARD_HPP

View File

@@ -0,0 +1,197 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#include "b2outputparser.h"
#include "b2utility.h"
#include "external/projectexplorer/gccparser.h"
#include "external/projectexplorer/ldparser.h"
#include "external/projectexplorer/clangparser.h"
// Qt Creator
#include <projectexplorer/ioutputparser.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/task.h>
#include <utils/qtcassert.h>
// Qt
#include <QRegExp>
#include <QString>
#include <QStringRef>
namespace BoostBuildProjectManager {
namespace Internal {
namespace {
char const* const RxToolsetFromCommand = "^([\\w-]+)(?:\\.)([\\w-]+).+$";
char const* const RxToolsetFromWarning = "^warning\\:.+toolset.+\\\"([\\w-]+)\\\".*$";
// TODO: replace filename with full path? ^\*\*passed\*\*\s(.+\.test)
char const* const RxTestPassed = "^\\*\\*passed\\*\\*\\s+(.+\\.test)\\s*$";
char const* const RxTestFailed = "^\\.\\.\\.failed\\s+testing\\.capture-output\\s+(.+\\.run)\\.\\.\\.$";
char const* const RxTestFailedAsExpected = "^\\(failed-as-expected\\)\\s+(.+\\.o)\\s*$";
char const* const RxTestFileLineN = "(\\.[ch][px]*)\\(([0-9]+)\\)"; // replace \\1:\\2
char const* const RxTestFileObj = "\\W(\\w+)(\\.o)";
}
BoostBuildParser::BoostBuildParser()
: rxToolsetNameCommand_(QLatin1String(RxToolsetFromCommand))
, rxToolsetNameWarning_(QLatin1String(RxToolsetFromWarning))
, rxTestPassed_(QLatin1String(RxTestPassed))
, rxTestFailed_(QLatin1String(RxTestFailed))
, rxTestFailedAsExpected_(QLatin1String(RxTestFailedAsExpected))
, rxTestFileLineN_(QLatin1String(RxTestFileLineN))
, rxTestFileObj_(QLatin1String(RxTestFileObj))
, lineMode_(Common)
{
rxToolsetNameCommand_.setMinimal(true);
QTC_CHECK(rxToolsetNameCommand_.isValid());
rxToolsetNameWarning_.setMinimal(true);
QTC_CHECK(rxToolsetNameWarning_.isValid());
rxTestPassed_.setMinimal(true);
QTC_CHECK(rxTestPassed_.isValid());
rxTestFailed_.setMinimal(true);
QTC_CHECK(rxTestFailed_.isValid());
rxTestFailedAsExpected_.setMinimal(true);
QTC_CHECK(rxTestFailedAsExpected_.isValid());
rxTestFileLineN_.setMinimal(true);
QTC_CHECK(rxTestFileLineN_.isValid());
}
QString BoostBuildParser::findToolset(QString const& line) const
{
QString name;
if (rxToolsetNameWarning_.indexIn(line) > -1)
name = rxToolsetNameWarning_.cap(1);
else if (rxToolsetNameCommand_.indexIn(line) > -1)
name = rxToolsetNameCommand_.cap(1);
return name;
}
void BoostBuildParser::setToolsetParser(QString const& toolsetName)
{
if (!toolsetName.isEmpty() && toolsetName_.isEmpty()) {
if (QStringRef(&toolsetName, 0, 3) == QLatin1String("gcc")) {
appendOutputParser(new ProjectExplorer::GccParser());
toolsetName_ = toolsetName;
} else if (QStringRef(&toolsetName, 0, 5) == QLatin1String("clang")) {
// clang-xxx found (e.g. clang-linux)
appendOutputParser(new ProjectExplorer::ClangParser());
toolsetName_ = toolsetName;
} else {
// TODO: Add more toolsets (Intel, VC++, etc.)
}
}
}
void BoostBuildParser::stdOutput(QString const& rawLine)
{
setToolsetParser(findToolset(rawLine));
QString const line
= rightTrimmed(rawLine).replace(rxTestFileLineN_, QLatin1String("\\1:\\2"));
if (!toolsetName_.isEmpty() && line.startsWith(toolsetName_))
lineMode_ = Toolset;
else if (line.startsWith(QLatin1String("testing"))
|| line.startsWith(QLatin1String("(failed-as-expected)")))
lineMode_ = Testing;
else if (line.startsWith(QLatin1String("common")))
lineMode_ = Common;
// TODO: Why forwarding stdOutput to ProjectExplorer::IOutputParser::stdError?
// Because of a bug (or feature?) in Boost.Build:
// stdout and stderr not forwarded to respective channels
// https://svn.boost.org/trac/boost/ticket/9485
if (lineMode_ == Toolset) {
ProjectExplorer::IOutputParser::stdError(line);
} else if (lineMode_ == Testing) {
if (rxTestPassed_.indexIn(line) > -1) {
BBPM_QDEBUG(rxTestPassed_.capturedTexts());
// TODO: issue #3
ProjectExplorer::Task task(ProjectExplorer::Task::Unknown
, rxTestPassed_.cap(0)
, Utils::FileName::fromString(rxTestPassed_.cap(1))
, -1 // line
, ProjectExplorer::Constants::TASK_CATEGORY_COMPILE);
setTask(task);
lineMode_ = Common;
} else if (rxTestFailed_.indexIn(line) > -1) {
BBPM_QDEBUG(rxTestFailed_.capturedTexts());
// Report summary task for "...failed testing.capture-output /myfile.run"
ProjectExplorer::Task task(ProjectExplorer::Task::Error
, rxTestFailed_.cap(0)
, Utils::FileName::fromString(rxTestFailed_.cap(1))
, -1 // line
, ProjectExplorer::Constants::TASK_CATEGORY_COMPILE);
setTask(task);
lineMode_ = Common;
} else if (rxTestFailedAsExpected_.indexIn(line) > -1) {
BBPM_QDEBUG(rxTestFailedAsExpected_.capturedTexts());
// TODO: Handling of "(failed-as-expected)" is not great, might be confusing.
// Boost.Build spits out compile command first, so compilation errors arrive
// and are parsed immediately (issue tasks are created)
// due to lineMode_==Toolset.
// Then, "(failed-as-expected)" status arrives and there seem to be no way to
// look back and clear all the issue tasks created for compilation errors.
// TODO: Ask Volodya if b2 could announce "testing." before compile command
// for a compile-time test.
QString fileName(rxTestFailedAsExpected_.cap(1));
if (rxTestFileObj_.indexIn(fileName))
fileName = rxTestFileObj_.cap(1) + QLatin1String(".cpp");// FIXME:hardcoded ext
// ATM, we can only indicate in UI that test failed-as-expected
ProjectExplorer::Task task(ProjectExplorer::Task::Error
, rxTestFailedAsExpected_.cap(0)
, Utils::FileName::fromString(fileName)
, -1 // line
, ProjectExplorer::Constants::TASK_CATEGORY_COMPILE);
setTask(task);
lineMode_ = Common;
} else {
// Parses compilation errors of run-time tests, creates issue tasks
ProjectExplorer::IOutputParser::stdError(line);
}
} else {
doFlush();
ProjectExplorer::IOutputParser::stdOutput(line);
}
}
void BoostBuildParser::stdError(QString const& line)
{
setToolsetParser(findToolset(line));
ProjectExplorer::IOutputParser::stdError(line);
}
void BoostBuildParser::doFlush()
{
if (!lastTask_.isNull()) {
ProjectExplorer::Task t = lastTask_;
lastTask_.clear();
emit addTask(t);
}
}
void BoostBuildParser::setTask(ProjectExplorer::Task const& task)
{
doFlush();
lastTask_ = task;
}
} // namespace Internal
} // namespace BoostBuildProjectManager

View File

@@ -0,0 +1,64 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBOUTPUTPARSER_HPP
#define BBOUTPUTPARSER_HPP
// Qt Creator
#include <projectexplorer/ioutputparser.h>
#include <projectexplorer/task.h>
// Qt
#include <QPointer>
#include <QString>
namespace BoostBuildProjectManager {
namespace Internal {
class BoostBuildParser : public ProjectExplorer::IOutputParser
{
Q_OBJECT
public:
BoostBuildParser();
void stdOutput(QString const& line);
void stdError(QString const& line);
protected:
void doFlush();
private:
QString findToolset(QString const& line) const;
void setToolsetParser(QString const& toolsetName);
void setTask(ProjectExplorer::Task const& task);
QRegExp rxToolsetNameCommand_; // ".compile." command line
QRegExp rxToolsetNameWarning_; // "warning: " status line
QRegExp rxTestPassed_; // "**passed**" status line
QRegExp rxTestFailed_; // "...failed testing" status line
QRegExp rxTestFailedAsExpected_; // "(failed-as-expected)" status line
QRegExp rxTestFileLineN_; // file.cpp(XX) => file.cpp:XX
QRegExp rxTestFileObj_; // file.o => file.cpp
QString toolsetName_;
// Boost.Build command mode relates to first command token in line.
enum LineMode { Common, Toolset, Testing };
LineMode lineMode_;
ProjectExplorer::Task lastTask_;
QPointer<ProjectExplorer::IOutputParser> parser_;
};
} // namespace Internal
} // namespace BoostBuildProjectManager
#endif // BBOUTPUTPARSER_HPP

View File

@@ -0,0 +1,306 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#include "b2buildconfiguration.h"
#include "b2buildstep.h"
#include "b2openprojectwizard.h"
#include "b2project.h"
#include "b2projectfile.h"
#include "b2projectmanager.h"
#include "b2projectmanagerconstants.h"
#include "b2projectnode.h"
#include "b2utility.h"
// Qt Creator
#include <app/app_version.h>
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/generatedfile.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/cppprojects.h>
#include <cpptools/cpptoolsconstants.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/target.h>
#include <qtsupport/customexecutablerunconfiguration.h>
#include <utils/fileutils.h>
#include <utils/QtConcurrentTools>
// Qt
#include <QDir>
#include <QFileInfo>
#include <QMessageBox>
namespace BoostBuildProjectManager {
namespace Internal {
Project::Project(ProjectManager* manager, QString const& fileName)
: manager_(manager)
, filePath_(fileName)
, projectFile_(new ProjectFile(this, filePath_)) // enables projectDirectory()
, projectNode_(new ProjectNode(this, projectFile_))
{
Q_ASSERT(manager_);
Q_ASSERT(!filePath_.isEmpty());
setProjectContext(Core::Context(Constants::PROJECT_CONTEXT));
setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX));
#if defined(IDE_VERSION_MAJOR) && (IDE_VERSION_MAJOR == 3 && IDE_VERSION_MINOR > 0)
setId(Constants::PROJECT_ID);
#endif
QFileInfo const projectFileInfo(filePath_);
QDir const projectDir(projectFileInfo.dir());
projectName_ = defaultProjectName(filePath_);
filesFilePath_ = QFileInfo(projectDir
, filePath_ + QLatin1String(Constants::EXT_JAMFILE_FILES)).absoluteFilePath();
includesFilePath_ = QFileInfo(projectDir
, filePath_ + QLatin1String(Constants::EXT_JAMFILE_INCLUDES)).absoluteFilePath();
projectNode_->setDisplayName(displayName());
manager_->registerProject(this);
// TODO: Add file watchers
//projectFileWatcher_->addPath(projectFilePath);
//connect(projectFileWatcher_, SIGNAL(fileChanged(QString)), this, SLOT(refresh()));
BBPM_QDEBUG("created project: " << displayName() << " in " << projectDirectory());
}
Project::~Project()
{
manager_->unregisterProject(this);
delete projectNode_;
}
QString Project::displayName() const
{
return projectName_;
}
#if defined(IDE_VERSION_MAJOR) && (IDE_VERSION_MAJOR == 3 && IDE_VERSION_MINOR == 0)
Core::Id Project::id() const
{
return Core::Id(Constants::PROJECT_ID);
}
#endif
Core::IDocument* Project::document() const
{
return projectFile_;
}
ProjectExplorer::IProjectManager* Project::projectManager() const
{
return manager_;
}
ProjectExplorer::ProjectNode* Project::rootProjectNode() const
{
return projectNode_;
}
QStringList Project::files(FilesMode fileMode) const
{
// TODO: handle ExcludeGeneratedFiles, but what files exactly?
// *.qtcreator.files, *.qtcreator.includes and *.user?
Q_UNUSED(fileMode);
return files_;
}
QStringList Project::files() const
{
return files(FilesMode::AllFiles);
}
QString Project::filesFilePath() const
{
Q_ASSERT(!filesFilePath_.isEmpty());
return filesFilePath_;
}
QString Project::includesFilePath() const
{
Q_ASSERT(!includesFilePath_.isEmpty());
return includesFilePath_;
}
bool Project::needsConfiguration() const
{
// TODO: Does Boost.Build project require any configuration on loading?
// - Kit selection
// - build/stage directory
// - targets listing
// CMakeProjectManager seems to request configuration in fromMap()
return false;
}
/*static*/
QString Project::defaultProjectName(QString const& fileName)
{
QFileInfo const fileInfo(fileName);
return fileInfo.absoluteDir().dirName();
}
/*static*/
QString Project::defaultBuildDirectory(QString const& top)
{
Utils::FileName fn(Utils::FileName::fromString(defaultWorkingDirectory(top)));
fn.appendPath(BBPM_C(BUILD_DIR_NAME));
return fn.toString();
}
/*static*/
QString Project::defaultWorkingDirectory(QString const& top)
{
// Accepts both, project file or project directory, as top.
return ProjectExplorer::Project::projectDirectory(Utils::FileName::fromString(top)).toString();
}
void Project::setProjectName(QString const& name)
{
if (projectName_ != name) {
projectName_ = name;
projectNode_->setDisplayName(projectName_);
// TODO: signal?
}
}
QVariantMap Project::toMap() const
{
QVariantMap map(ProjectExplorer::Project::toMap());
map.insert(QLatin1String(Constants::P_KEY_PROJECTNAME), projectName_);
return map;
}
// This function is called at the very beginning to restore the settings
// from .user file, if there is any with previous settings stored.
bool Project::fromMap(QVariantMap const& map)
{
BBPM_QDEBUG(displayName());
QTC_ASSERT(projectNode_, return false);
if (!ProjectExplorer::Project::fromMap(map))
return false;
QVariantMap extraValues(map);
if (!extraValues.contains(BBPM_C(P_KEY_PROJECTNAME)))
extraValues.insert(BBPM_C(P_KEY_PROJECTNAME), projectName_);
setProjectName(map.value(BBPM_C(P_KEY_PROJECTNAME)).toString());
// Check required auxiliary files, run wizard of necessary
if (!QFileInfo(filesFilePath()).exists() || !QFileInfo(includesFilePath()).exists()) {
ProjectExplorer::Kit* defaultKit = ProjectExplorer::KitManager::defaultKit();
Q_ASSERT(defaultKit);
OpenProjectWizard wizard(this);
if (!wizard.run(defaultKit->displayName(), extraValues))
return false;
QVariantMap outputValues = wizard.outputValues();
setProjectName(outputValues.value(BBPM_C(P_KEY_PROJECTNAME)).toString());
}
// Set up active ProjectConfiguration (aka Target).
// NOTE: Call setActiveBuildConfiguration when creating new build configurations.
if (!activeTarget()) {
// Create project configuration from scratch
// TODO: Map the Kit to Boost.Build toolset option value
ProjectExplorer::Kit* defaultKit = ProjectExplorer::KitManager::defaultKit();
Q_ASSERT(defaultKit);
// Creates as many {Build|Run|Deploy}Configurations for as corresponding
// factories report as available.
// For example, BuildConfigurationFactory::availableBuilds => Debug and Release
ProjectExplorer::Target* target = createTarget(defaultKit);
QTC_ASSERT(target, return false);
addTarget(target);
} else {
// Configure project from settings sorced from .user file
// TODO: Do we need to handle anything from .user here? Do we need this case?
BBPM_QDEBUG(displayName() << "has user file");
}
// Sanity check (taken from GenericProjectManager):
// We need both a BuildConfiguration and a RunConfiguration!
QList<ProjectExplorer::Target*> targetList = targets();
foreach (ProjectExplorer::Target* t, targetList) {
if (!t->activeBuildConfiguration()) {
removeTarget(t);
continue;
}
if (!t->activeRunConfiguration())
t->addRunConfiguration(new QtSupport::CustomExecutableRunConfiguration(t));
}
QTC_ASSERT(hasActiveBuildSettings(), return false);
QTC_ASSERT(activeTarget() != 0, return false);
// Trigger loading project tree and parsing sources
refresh();
return true;
}
void Project::refresh()
{
QTC_ASSERT(QFileInfo(filesFilePath()).exists(), return);
QTC_ASSERT(QFileInfo(includesFilePath()).exists(), return);
QSet<QString> oldFileList;
oldFileList = files_.toSet();
// Parse project:
// The manager does not parse Jamfile files.
// Only generates and parses list of source files in Jamfile.${JAMFILE_FILES_EXT}
QString const projectPath(projectDirectory().toString());
filesRaw_ = Utility::readLines(filesFilePath());
files_ = Utility::makeAbsolutePaths(projectPath, filesRaw_);
QStringList includePaths =
Utility::makeAbsolutePaths(projectPath,
Utility::readLines(includesFilePath()));
emit fileListChanged();
projectNode_->refresh(oldFileList);
// TODO: Does it make sense to move this to separate asynchronous task?
// TODO: extract updateCppCodeModel
CppTools::CppModelManager *modelmanager =
CppTools::CppModelManager::instance();
if (modelmanager) {
CppTools::ProjectInfo pinfo(this);
CppTools::ProjectPartBuilder builder(pinfo);
//builder.setDefines(); // TODO: waiting for Jamfile parser
builder.setIncludePaths(QStringList() << projectDirectory().toString() << includePaths);
const QList<Core::Id> languages = builder.createProjectPartsForFiles(files_);
foreach (Core::Id language, languages)
setProjectLanguage(language, true);
cppModelFuture_.cancel();
pinfo.finish();
cppModelFuture_ = modelmanager->updateProjectInfo(pinfo);
}
}
} // namespace Internal
} // namespace BoostBuildProjectManager

View File

@@ -0,0 +1,106 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBPROJECT_HPP
#define BBPROJECT_HPP
// Qt Creator
#include <app/app_version.h>
#include <coreplugin/idocument.h>
#include <projectexplorer/project.h>
#include <utils/fileutils.h>
// Qt
#include <QString>
#include <QFuture>
#include <QFutureInterface>
namespace BoostBuildProjectManager {
namespace Internal {
class ProjectFile;
class ProjectManager;
class ProjectNode;
// Represents a project node in the project explorer.
class Project : public ProjectExplorer::Project
{
Q_OBJECT
public:
Project(ProjectManager* manager, QString const& fileName);
~Project();
#if defined(IDE_VERSION_MAJOR) && (IDE_VERSION_MAJOR == 3 && IDE_VERSION_MINOR == 0)
Core::Id id() const;
#endif
QString displayName() const;
Core::IDocument* document() const;
ProjectExplorer::IProjectManager* projectManager() const;
ProjectExplorer::ProjectNode* rootProjectNode() const;
QStringList files(FilesMode fileMode) const;
bool needsConfiguration() const;
void refresh();
QString filesFilePath() const;
QStringList files() const;
QString includesFilePath() const;
static QString defaultProjectName(QString const& fileName);
static QString defaultBuildDirectory(QString const& top);
static QString defaultWorkingDirectory(QString const& top);
protected:
QVariantMap toMap() const;
// Deserializes all project data from the map object
// Calls the base ProjectExplorer::Project::fromMap function first.
bool fromMap(QVariantMap const& map);
private:
void setProjectName(QString const& name);
// Corresponding project manager passed to the constructor
ProjectManager* manager_;
// By default, name of directory with Jamfile.
// Boost.Build treats each Jamfile is a separate project,
// where hierarchy of Jamfiles makes hierarchy of projects.
QString projectName_;
// Jamfile passed to the constructor (Jamroot, Jamfile, Jamfile.v2).
QString filePath_;
// Auxiliary file Jamfile.${JAMFILE_FILES_EXT} with list of source files.
// Role of this file is similar to the .files file in the GenericProjectManager,
// hence managing of this file is implemented in very similar manner.
QString filesFilePath_;
QStringList files_;
QStringList filesRaw_;
QHash<QString, QString> entriesRaw_;
// Auxiliary file Jamfile.${JAMFILE_INCLUDES_EXT} with list of source files.
QString includesFilePath_;
ProjectFile* projectFile_;
ProjectNode* projectNode_;
QFuture<void> cppModelFuture_;
};
} // namespace Internal
} // namespace BoostBuildProjectManager
#endif // BBPROJECT_HPP

View File

@@ -0,0 +1,80 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#include "b2project.h"
#include "b2projectfile.h"
#include "b2projectmanagerconstants.h"
#include "b2utility.h"
namespace BoostBuildProjectManager {
namespace Internal {
ProjectFile::ProjectFile(Project* project, QString const& fileName)
: Core::IDocument(project)
, project_(project)
{
Q_ASSERT(!fileName.isEmpty());
setFilePath(Utils::FileName::fromString(fileName));
BBPM_QDEBUG(fileName);
}
bool ProjectFile::save(QString* errorString, QString const& fileName, bool autoSave)
{
Q_UNUSED(errorString);
Q_UNUSED(fileName);
Q_UNUSED(autoSave);
BBPM_QDEBUG("TODO");
return false;
}
QString ProjectFile::defaultPath() const
{
BBPM_QDEBUG("TODO");
return QString();
}
QString ProjectFile::suggestedFileName() const
{
return QString();
}
QString ProjectFile::mimeType() const
{
BBPM_QDEBUG("TODO");
return QLatin1String(Constants::MIMETYPE_JAMFILE);
}
bool ProjectFile::isModified() const
{
return false;
}
bool ProjectFile::isSaveAsAllowed() const
{
BBPM_QDEBUG("TODO");
return false;
}
bool ProjectFile::reload(QString* errorString, ReloadFlag flag, ChangeType type)
{
Q_UNUSED(errorString);
Q_UNUSED(flag);
Q_UNUSED(type);
BBPM_QDEBUG("TODO");
return false;
}
} // namespace Internal
} // namespace AutotoolsProjectManager

View File

@@ -0,0 +1,47 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBPROJECTFILE_HPP
#define BBPROJECTFILE_HPP
#include <coreplugin/idocument.h>
namespace BoostBuildProjectManager {
namespace Internal {
class Project;
// Describes the root of a project for use in Project class.
// In the context of Boost.Build the implementation is mostly empty,
// as the modification of a project is done by editing Jamfile.v2 files.
class ProjectFile : public Core::IDocument
{
Q_OBJECT
public:
ProjectFile(Project* project, QString const& fileName);
bool save(QString* errorString, QString const& fileName, bool autoSave);
QString defaultPath() const;
QString suggestedFileName() const;
QString mimeType() const;
bool isModified() const;
bool isSaveAsAllowed() const;
bool reload(QString* errorString, ReloadFlag flag, ChangeType type);
private:
Project* project_;
};
} // namespace Internal
} // namespace AutotoolsProjectManager
#endif // BBPROJECTFILE_HPP

View File

@@ -0,0 +1,64 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#include "b2projectmanager.h"
#include "b2projectmanagerconstants.h"
#include "b2project.h"
#include "b2utility.h"
// Qt Creator
#include <projectexplorer/iprojectmanager.h>
// Qt
#include <QFileInfo>
#include <QString>
namespace BoostBuildProjectManager {
namespace Internal {
ProjectManager::ProjectManager()
{
}
QString ProjectManager::mimeType() const
{
BBPM_QDEBUG(Constants::MIMETYPE_PROJECT);
return QLatin1String(Constants::MIMETYPE_PROJECT);
}
ProjectExplorer::Project*
ProjectManager::openProject(QString const& fileName, QString* errorString)
{
BBPM_QDEBUG("opening project:" << fileName);
if (!QFileInfo(fileName).isFile()) {
if (errorString)
*errorString = tr("Failed opening project \"%1\": Project is not a file.")
.arg(fileName);
return 0;
}
return new Project(this, fileName);
}
void ProjectManager::registerProject(Project* project)
{
Q_ASSERT(project);
projects_.append(project);
}
void ProjectManager::unregisterProject(Project* project)
{
Q_ASSERT(project);
projects_.removeAll(project);
}
} // namespace Internal
} // namespace BoostBuildProjectManager

View File

@@ -0,0 +1,54 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBPROJECTMANAGER_HPP
#define BBPROJECTMANAGER_HPP
// Qt Creator
#include <projectexplorer/iprojectmanager.h>
// Qt
#include <QList>
#include <QString>
namespace ProjectExplorer {
class Project;
}
namespace BoostBuildProjectManager {
namespace Internal {
class Project;
// Sole implementation of the IProjectManager class for the extension.
class ProjectManager : public ProjectExplorer::IProjectManager
{
Q_OBJECT
public:
ProjectManager();
QString mimeType() const;
// Creates new instance of Project class.
ProjectExplorer::Project*
openProject(QString const& fileName, QString* errorString);
void registerProject(Project* project);
void unregisterProject(Project* project);
private:
QList<Project*> projects_;
};
} // namespace Internal
} // namespace BoostBuildProjectManager
#endif // BBPROJECTMANAGER_HPP

View File

@@ -0,0 +1,23 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBPROJECTMANAGER_GLOBAL_HPP
#define BBPROJECTMANAGER_GLOBAL_HPP
#include <QtGlobal>
#if defined(BOOSTBUILDPROJECTMANAGER_LIBRARY)
# define BOOSTBUILDPROJECTMANAGER_EXPORT Q_DECL_EXPORT
#else
# define BOOSTBUILDPROJECTMANAGER_EXPORT Q_DECL_IMPORT
#endif
#endif // BBPROJECTMANAGER_GLOBAL_HPP

View File

@@ -0,0 +1,68 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBPROJECTMANAGERCONSTANTS_HPP
#define BBPROJECTMANAGERCONSTANTS_HPP
#include <qglobal.h>
namespace BoostBuildProjectManager {
namespace Constants {
char const BOOSTBUILD[] = "Boost.Build";
char const PROJECT_CONTEXT[] = "BoostBuildProjectManager.ProjectContext";
char const PROJECT_ID[] = "BoostBuildProjectManager.Project";
char const PROJECT_WIZARD_ID[] = "BoostBuildProjectManager.Project.Wizard";
char const PROJECT_READER_TASK_ID[] = "BoostBuildProjectManager.ProjectReader.Task";
char const BUILDCONFIGURATION_ID[] = "BoostBuildProjectManager.BuildConfiguration";
char const BUILDSTEP_ID[] = "BoostBuildProjectManager.BuildStep";
// Keys for class map registry
char const P_KEY_PROJECTNAME[] = "BoostBuildProjectManager.Project.ProjectName";
char const BC_KEY_WORKDIR[] = "BoostBuildProjectManager.BuildConfiguration.WorkingDirectory";
char const BS_KEY_CLEAN[] = "BoostBuildProjectManager.BuildStep.Clean";
char const BS_KEY_ARGUMENTS[] = "BoostBuildProjectManager.BuildStep.AdditionalArguments";
// MIME types and file patterns
char const MIMETYPE_PROJECT[] = "text/x-boostbuild-project";
char const MIMETYPE_JAMFILE[] = "application/vnd.boostbuild.v2";
char const MIMETYPE_JAMFILE_FILES[] = "application/vnd.qtcreator.boostbuild.files";
const char MIMETYPE_JAMFILE_INCLUDES[] = "application/vnd.qtcreator.boostbuild.includes";
char const EXT_JAMFILE_FILES[] = ".qtcreator.files";
char const EXT_JAMFILE_INCLUDES[] = ".qtcreator.includes";
// Command and options
char const COMMAND_BB2[] = "b2";
char const COMMAND_BJAM[] = "bjam";
char const VARIANT_DEBUG[] = QT_TR_NOOP("Debug");
char const VARIANT_RELEASE[] = QT_TR_NOOP("Release");
// ${BOOST}/tools/build/v2/doc/src/architecture.xml
// Since Boost.Build almost always generates targets under the "bin"
char const BUILD_DIR_NAME[] = "bin";
// FileSelectionWizardPage
char const HIDE_FILE_FILTER_SETTING[] = "BoostBuildProjectManager/FileFilter";
char const HIDE_FILE_FILTER_DEFAULT[] = "Makefile*; *.o; *.obj; *~; *.files; *.config; *.creator; *.user; *.includes; *.autosave";
char const SHOW_FILE_FILTER_SETTING[] = "BoostBuildProjectManager/ShowFileFilter";
char const SHOW_FILE_FILTER_DEFAULT[] = "*.c; *.cc; *.cpp; *.cp; *.cxx; *.c++; *.h; *.hh; *.h; *.hxx;";
// Meaningful names for common boolean values
bool const FileNotGenerated = false;
bool const ReturnValueNotIgnored = false;
} // namespace Constants
} // namespace BoostBuildProjectManager
#endif // BBPROJECTMANAGERCONSTANTS_HPP

View File

@@ -0,0 +1,83 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#include "b2buildconfiguration.h"
#include "b2buildstep.h"
#include "b2projectmanager.h"
#include "b2projectmanagerplugin.h"
#include "b2projectmanagerconstants.h"
#include <coreplugin/icore.h>
#include <coreplugin/icontext.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/coreconstants.h>
#include <utils/mimetypes/mimedatabase.h>
// Qt
#include <QAction>
#include <QMessageBox>
#include <QMainWindow>
#include <QMenu>
#include <QtPlugin>
namespace BoostBuildProjectManager { namespace Internal {
BoostBuildPlugin::BoostBuildPlugin()
{
// Create your members
}
BoostBuildPlugin::~BoostBuildPlugin()
{
// Unregister objects from the plugin manager's object pool
// Delete members
}
bool BoostBuildPlugin::initialize(QStringList const& arguments, QString* errorString)
{
Q_UNUSED(arguments)
Q_UNUSED(errorString)
// Register objects in the plugin manager's object pool
// Load settings
// Add actions to menus
// Connect to other plugins' signals
// In the initialize function, a plugin can be sure that the plugins it
// depends on have initialized their members.
QLatin1String const mimeTypes(":boostbuildproject/BoostBuildProjectManager.mimetypes.xml");
Utils::MimeDatabase::addMimeTypes(mimeTypes);
addAutoReleasedObject(new BuildStepFactory);
addAutoReleasedObject(new BuildConfigurationFactory);
//TODO addAutoReleasedObject(new RunConfigurationFactory);
addAutoReleasedObject(new ProjectManager);
return true;
}
void BoostBuildPlugin::extensionsInitialized()
{
// Retrieve objects from the plugin manager's object pool
// In the extensionsInitialized function, a plugin can be sure that all
// plugins that depend on it are completely initialized.
}
ExtensionSystem::IPlugin::ShutdownFlag BoostBuildPlugin::aboutToShutdown()
{
// Save settings
// Disconnect from signals that are not needed during shutdown
// Hide UI (if you add UI that is not in the main window directly)
return SynchronousShutdown;
}
}}

View File

@@ -0,0 +1,48 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBPROJECTMANAGERPLUGIN_HPP
#define BBPROJECTMANAGERPLUGIN_HPP
#include "b2projectmanager_global.h"
#include <extensionsystem/iplugin.h>
namespace BoostBuildProjectManager {
namespace Internal {
// Sole implementation of the IPlugin class for the extension.
class BoostBuildPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "BoostBuildProjectManager.json")
public:
BoostBuildPlugin();
~BoostBuildPlugin();
// Called as second step of the plugins loading.
// The initialize functions are called in root-to-leaf order of the dependency tree.
bool initialize(QStringList const& arguments, QString* errorString);
// Called as final step of the plugins loading.
// At this point, all dependencies along the plugin dependency tree
// have been initialized completely.
void extensionsInitialized();
ShutdownFlag aboutToShutdown();
};
} // namespace Internal
} // namespace BoostBuildProjectManager
#endif // BBPROJECTMANAGERPLUGIN_HPP

View File

@@ -0,0 +1,260 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#include "b2projectnode.h"
#include "b2project.h"
#include "b2utility.h"
// Qt Creator
#include <coreplugin/idocument.h>
#include <projectexplorer/projectnodes.h>
#include <utils/qtcassert.h>
// Qt
#include <QFutureInterface>
#include <QHash>
#include <QList>
#include <QSet>
#include <QString>
#include <QStringList>
namespace BoostBuildProjectManager {
namespace Internal {
ProjectNode::ProjectNode(Project* project, Core::IDocument* projectFile)
: ProjectExplorer::ProjectNode(projectFile->filePath())
, project_(project)
, projectFile_(projectFile)
{
// TODO: setDisplayName(QFileInfo(projectFile->filePath()).completeBaseName());
}
bool ProjectNode::hasBuildTargets() const
{
return false;
}
QList<ProjectExplorer::ProjectAction>
ProjectNode::supportedActions(Node* node) const
{
Q_UNUSED(node);
// TODO: Jamfiles (auto)editing not supported, does it make sense to manage files?
return QList<ProjectExplorer::ProjectAction>();
}
bool ProjectNode::canAddSubProject(QString const& filePath) const
{
Q_UNUSED(filePath)
return false;
}
bool ProjectNode::addSubProjects(QStringList const& filePaths)
{
Q_UNUSED(filePaths)
return false;
}
bool ProjectNode::removeSubProjects(QStringList const& filePath)
{
Q_UNUSED(filePath)
return false;
}
bool ProjectNode::addFiles(QStringList const& filePaths, QStringList* notAdded = 0)
{
Q_UNUSED(filePaths);
Q_UNUSED(notAdded);
return false;
}
bool ProjectNode::removeFiles(QStringList const& filePaths, QStringList* notRemoved = 0)
{
Q_UNUSED(filePaths);
Q_UNUSED(notRemoved);
return false;
}
bool ProjectNode::deleteFiles(QStringList const& filePaths)
{
Q_UNUSED(filePaths);
return false;
}
bool ProjectNode::renameFile(QString const& filePath, QString const& newFilePath)
{
Q_UNUSED(filePath);
Q_UNUSED(newFilePath);
return false;
}
QList<ProjectExplorer::RunConfiguration*> ProjectNode::runConfigurationsFor(Node* node)
{
Q_UNUSED(node);
return QList<ProjectExplorer::RunConfiguration*>();
}
void ProjectNode::refresh(QSet<QString> oldFileList)
{
// The idea of refreshing files in project explorer taken from GenericProjectManager
// Only do this once, at first run.
if (oldFileList.isEmpty()) {
using ProjectExplorer::FileNode;
FileNode* projectFileNode = new FileNode(project_->projectFilePath()
, ProjectExplorer::ProjectFileType
, Constants::FileNotGenerated);
FileNode* filesFileNode = new FileNode(Utils::FileName::fromString(project_->filesFilePath())
, ProjectExplorer::ProjectFileType
, Constants::FileNotGenerated);
FileNode* includesFileNode = new FileNode(Utils::FileName::fromString(project_->includesFilePath())
, ProjectExplorer::ProjectFileType
, Constants::FileNotGenerated);
addFileNodes(QList<FileNode*>()
<< projectFileNode << filesFileNode << includesFileNode);
}
oldFileList.remove(project_->filesFilePath());
oldFileList.remove(project_->includesFilePath());
QSet<QString> newFileList = project_->files().toSet();
newFileList.remove(project_->filesFilePath());
newFileList.remove(project_->includesFilePath());
// Calculate set of added and removed files
QSet<QString> removed = oldFileList;
removed.subtract(newFileList);
QSet<QString> added = newFileList;
added.subtract(oldFileList);
typedef QHash<QString, QStringList> FilesInPaths;
typedef FilesInPaths::ConstIterator FilesInPathsIterator;
using ProjectExplorer::FileNode;
using ProjectExplorer::FileType;
QString const baseDir = QFileInfo(path().toString()).absolutePath();
// Process all added files
FilesInPaths filesInPaths = Utility::sortFilesIntoPaths(baseDir, added);
for (FilesInPathsIterator it = filesInPaths.constBegin(),
cend = filesInPaths.constEnd(); it != cend; ++it) {
// Create node
QString const& filePath = it.key();
QStringList parts;
if (!filePath.isEmpty())
parts = filePath.split(QLatin1Char('/'));
FolderNode* folder = findFolderByName(parts, parts.size());
if (!folder)
folder = createFolderByName(parts, parts.size());
// Attach content to node
QList<FileNode*> fileNodes;
foreach (QString const& file, it.value()) {
// TODO: handle various types, mime to FileType
FileType fileType = ProjectExplorer::SourceType;
FileNode* fileNode = new FileNode(Utils::FileName::fromString(file), fileType, Constants::FileNotGenerated);
fileNodes.append(fileNode);
}
addFileNodes(fileNodes);
}
// Process all removed files
filesInPaths = Utility::sortFilesIntoPaths(baseDir, removed);
for (FilesInPathsIterator it = filesInPaths.constBegin(),
cend = filesInPaths.constEnd(); it != cend; ++it) {
// Create node
QString const& filePath = it.key();
QStringList parts;
if (!filePath.isEmpty())
parts = filePath.split(QLatin1Char('/'));
FolderNode* folder = findFolderByName(parts, parts.size());
QList<FileNode*> fileNodes;
foreach (QString const& file, it.value()) {
foreach (FileNode* fn, folder->fileNodes())
if (fn->path() == Utils::FileName::fromString(file))
fileNodes.append(fn);
}
removeFileNodes(fileNodes);
}
// Clean up
foreach (FolderNode* fn, subFolderNodes())
removeEmptySubFolders(this, fn);
}
void ProjectNode::removeEmptySubFolders(FolderNode* parent, FolderNode* subParent)
{
foreach (FolderNode* fn, subParent->subFolderNodes())
removeEmptySubFolders(subParent, fn);
if (subParent->subFolderNodes().isEmpty() && subParent->fileNodes().isEmpty())
removeFolderNodes(QList<FolderNode*>() << subParent);
}
QString appendPathComponents(QStringList const& components, int const end)
{
QString folderName;
for (int i = 0; i < end; ++i) {
folderName.append(components.at(i));
folderName += QLatin1Char('/');
}
return folderName;
}
ProjectExplorer::FolderNode*
ProjectNode::createFolderByName(QStringList const& components, int const end)
{
if (end == 0)
return this;
using ProjectExplorer::FolderNode;
QString const baseDir = QFileInfo(path().toString()).path();
QString const folderName = appendPathComponents(components, end);
FolderNode* folder = new FolderNode(Utils::FileName::fromString(baseDir + QLatin1Char('/') + folderName));
folder->setDisplayName(components.at(end - 1));
FolderNode* parent = findFolderByName(components, end - 1);
if (!parent)
parent = createFolderByName(components, end - 1);
addFolderNodes(QList<FolderNode*>() << folder);
return folder;
}
ProjectExplorer::FolderNode*
ProjectNode::findFolderByName(QStringList const& components, int const end) const
{
if (end == 0)
return const_cast<ProjectNode*>(this);
using ProjectExplorer::FolderNode;
FolderNode *parent = findFolderByName(components, end - 1);
if (!parent)
return 0;
QString const folderName = appendPathComponents(components, end);
QString const baseDir = QFileInfo(path().toString()).path();
foreach (FolderNode* fn, parent->subFolderNodes()) {
if (fn->path() == Utils::FileName::fromString(baseDir + QLatin1Char('/') + folderName))
return fn;
}
return 0;
}
} // namespace Internal
} // namespace BoostBuildProjectManager

View File

@@ -0,0 +1,77 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBPROJECTNODE_HPP
#define BBPROJECTNODE_HPP
// Qt Creator
#include <projectexplorer/projectnodes.h>
// Qt
#include <QFutureInterface>
#include <QList>
#include <QSet>
#include <QString>
#include <QStringList>
namespace Core {
class IDocument;
}
namespace ProjectExplorer {
class RunConfiguration;
}
namespace BoostBuildProjectManager {
namespace Internal {
class Project;
// An in-memory presentation of a Project.
// Represents a file or a folder of the project tree.
// No special operations (addFiles(), removeFiles(), renameFile(), etc.) are offered.
class ProjectNode : public ProjectExplorer::ProjectNode
{
public:
ProjectNode(Project* project, Core::IDocument* projectFile);
bool hasBuildTargets() const;
QList<ProjectExplorer::ProjectAction> supportedActions(Node* node) const;
bool canAddSubProject(QString const& filePath) const;
bool addSubProjects(QStringList const& filePaths);
bool removeSubProjects(QStringList const& filePaths);
bool addFiles(QStringList const& filePaths, QStringList* notAdded /*= 0*/);
bool removeFiles(QStringList const& filePaths, QStringList* notRemoved /*= 0*/);
bool deleteFiles(QStringList const& filePaths);
bool renameFile(QString const& filePath, QString const& newFilePath);
QList<ProjectExplorer::RunConfiguration*> runConfigurationsFor(Node* node);
void refresh(QSet<QString> oldFileList);
private:
ProjectExplorer::FolderNode*
createFolderByName(QStringList const& components, int end);
ProjectExplorer::FolderNode*
findFolderByName(QStringList const& components, int end) const;
void removeEmptySubFolders(FolderNode* parent, FolderNode* subParent);
Project* project_;
Core::IDocument* projectFile_;
};
} // namespace Internal
} // namespace BoostBuildProjectManager
#endif // BBPROJECTNODE_HPP

View File

@@ -0,0 +1,151 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#include "b2utility.h"
// Qt Creator
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
// Qt
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QHash>
#include <QList>
#include <QRegExp>
#include <QSet>
#include <QString>
#include <QStringList>
namespace BoostBuildProjectManager {
namespace Utility {
QStringList readLines(QString const& filePath)
{
QFileInfo const fileInfo(filePath);
QStringList lines;
QFile file(fileInfo.absoluteFilePath());
if (file.open(QFile::ReadOnly)) {
QTextStream stream(&file);
forever {
QString line = stream.readLine();
if (line.isNull())
break;
lines.append(line);
}
}
return lines;
}
QStringList makeAbsolutePaths(QString const& basePath, QStringList const& paths)
{
QDir const baseDir(basePath);
QFileInfo fileInfo;
QStringList absolutePaths;
foreach (QString const& path, paths) {
QString trimmedPath = path.trimmed();
if (!trimmedPath.isEmpty()) {
trimmedPath = Utils::FileName::fromUserInput(trimmedPath).toString();
fileInfo.setFile(baseDir, trimmedPath);
if (fileInfo.exists()) {
QString const absPath = fileInfo.absoluteFilePath();
Q_ASSERT(!absPath.isEmpty());
absolutePaths.append(absPath);
}
}
}
absolutePaths.removeDuplicates();
return absolutePaths;
}
QStringList& makeRelativePaths(QString const& basePath, QStringList& paths)
{
QDir const baseDir(basePath);
for (QStringList::iterator it = paths.begin(), end = paths.end(); it != end; ++it)
*it = baseDir.relativeFilePath(*it);
return paths;
}
QHash<QString, QStringList> sortFilesIntoPaths(QString const& basePath
, QSet<QString> const& files)
{
QHash<QString, QStringList> filesInPath;
QDir const baseDir(basePath);
foreach (QString const& absoluteFileName, files) {
QFileInfo const fileInfo(absoluteFileName);
Utils::FileName absoluteFilePath = Utils::FileName::fromString(fileInfo.path());
QString relativeFilePath;
if (absoluteFilePath.isChildOf(baseDir)) {
relativeFilePath = absoluteFilePath.relativeChildPath(
Utils::FileName::fromString(basePath)).toString();
} else {
// `file' is not part of the project.
relativeFilePath = baseDir.relativeFilePath(absoluteFilePath.toString());
if (relativeFilePath.endsWith(QLatin1Char('/')))
relativeFilePath.chop(1);
}
filesInPath[relativeFilePath].append(absoluteFileName);
}
return filesInPath;
}
// Parses Jamfile and looks for project rule to extract project name.
// Boost.Build project rule has the following syntax:
// project id : attributes ;
// The project definition can span across multiple lines, including empty lines.
// but each syntax token must be separated with a whitespace..
QString parseJamfileProjectName(QString const& fileName)
{
QString projectName;
QFile file(fileName);
if (file.exists()) {
// Jamfile project rule tokens to search for
QString const ruleBeg(QLatin1String("project"));
QChar const ruleEnd(QLatin1Char(';'));
QChar const attrSep(QLatin1Char(':'));
QChar const tokenSep(QLatin1Char(' ')); // used to ensure tokens separation
QString projectDef; // buffer for complete project definition
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream stream(&file);
while (!stream.atEnd()) {
QString const line(stream.readLine());
if (projectDef.isEmpty() && line.trimmed().startsWith(ruleBeg))
projectDef.append(line + tokenSep);
else if (!projectDef.isEmpty())
projectDef.append(line + tokenSep);
if (projectDef.contains(attrSep) || projectDef.contains(ruleEnd))
break;
}
if (!projectDef.isEmpty()) {
QRegExp rx(QLatin1String("\\s*project\\s+([a-zA-Z\\-\\/]+)\\s+[\\:\\;]?"));
rx.setMinimal(true);
QTC_CHECK(rx.isValid());
if (rx.indexIn(projectDef) > -1)
projectName = rx.cap(1);
}
}
return projectName;
}
} // namespace Utility
} // namespace BoostBuildProjectManager

View File

@@ -0,0 +1,60 @@
//
// Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
//
// This file is part of Qt Creator Boost.Build plugin project.
//
// This is free software; you can redistribute and/or modify it under
// the terms of the GNU Lesser General Public License, Version 2.1
// as published by the Free Software Foundation.
// See accompanying file LICENSE.txt or copy at
// http://www.gnu.org/licenses/lgpl-2.1-standalone.html.
//
#ifndef BBUTILITY_HPP
#define BBUTILITY_HPP
#include "b2projectmanagerconstants.h"
// Qt
#include <QDebug>
#include <QHash>
#include <QSet>
#include <QString>
#include <QStringList>
//////////////////////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
#define BBPM_QDEBUG(msg) \
qDebug() \
<< "[" << BoostBuildProjectManager::Constants::BOOSTBUILD << "] " \
<< "(" << __PRETTY_FUNCTION__ << ")"; \
qDebug().nospace() << "\t" << msg
#else
#define BBPM_QDEBUG(msg)
#endif // _DEBUG
#define BBPM_C(CONSTANT) QLatin1String(BoostBuildProjectManager::Constants::CONSTANT)
//////////////////////////////////////////////////////////////////////////////////////////
namespace BoostBuildProjectManager {
namespace Utility {
// Read all lines from a file.
QStringList readLines(QString const& absoluteFileName);
// Converts the path from relative to the project to an absolute path.
QStringList makeAbsolutePaths(QString const& basePath, QStringList const& paths);
QStringList& makeRelativePaths(QString const& basePath, QStringList& paths);
QHash<QString, QStringList> sortFilesIntoPaths(QString const& basePath
, QSet<QString> const& files);
QString parseJamfileProjectName(QString const& fileName);
} // namespace Utility
} // namespace BoostBuildProjectManager
#endif // BBUTILITY_HPP

View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/boostbuildproject">
<file>BoostBuildProjectManager.mimetypes.xml</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,46 @@
include(../../qtcreatorplugin.pri)
HEADERS += \
b2buildconfiguration.h \
b2buildinfo.h \
b2buildstep.h \
b2openprojectwizard.h \
b2outputparser.h \
b2project.h \
b2projectfile.h \
b2projectmanager.h \
b2projectmanager_global.h \
b2projectmanagerconstants.h \
b2projectmanagerplugin.h \
b2projectnode.h \
b2utility.h \
filesselectionwizardpage.h \
selectablefilesmodel.h \
external/projectexplorer/clangparser.h \
external/projectexplorer/gccparser.h \
external/projectexplorer/ldparser.h
SOURCES += \
b2buildconfiguration.cpp \
b2buildinfo.cpp \
b2buildstep.cpp \
b2openprojectwizard.cpp \
b2outputparser.cpp \
b2project.cpp \
b2projectfile.cpp \
b2projectmanager.cpp \
b2projectmanagerplugin.cpp \
b2projectnode.cpp \
b2utility.cpp \
filesselectionwizardpage.cpp \
selectablefilesmodel.cpp \
external/projectexplorer/clangparser.cpp \
external/projectexplorer/gccparser.cpp \
external/projectexplorer/ldparser.cpp
RESOURCES += \
boostbuildproject.qrc
OTHER_FILES += \
$${QTC_PLUGIN_NAME}.mimetypes.xml \
$${QTC_PLUGIN_NAME}.pluginspec.in

View File

@@ -0,0 +1,12 @@
QTC_PLUGIN_NAME = BoostBuildProjectManager
QTC_LIB_DEPENDS += \
extensionsystem \
utils
QTC_PLUGIN_DEPENDS += \
coreplugin \
projectexplorer \
cpptools \
texteditor \
qtsupport
QTC_TEST_DEPENDS += \
cppeditor

View File

@@ -0,0 +1,270 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License Version 2.1 by the Free Software
** Foundation and appearing in the file LICENSE.txt included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License Version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "clangparser.h"
#include "ldparser.h"
#include <projectexplorer/projectexplorerconstants.h>
using namespace ProjectExplorer;
// opt. drive letter + filename: (2 brackets)
static const char * const FILE_PATTERN = "(<command line>|([A-Za-z]:)?[^:]+\\.[^:]+)";
ClangParser::ClangParser() :
m_commandRegExp(QLatin1String("^clang(\\+\\+)?: +(fatal +)?(warning|error|note): (.*)$")),
m_inLineRegExp(QLatin1String("^In (.*) included from (.*):(\\d+):$")),
m_messageRegExp(QLatin1String("^") + QLatin1String(FILE_PATTERN) + QLatin1String("(:(\\d+):\\d+|\\((\\d+)\\) *): +(fatal +)?(error|warning|note): (.*)$")),
m_summaryRegExp(QLatin1String("^\\d+ (warnings?|errors?)( and \\d (warnings?|errors?))? generated.$")),
m_codesignRegExp(QLatin1String("^Code ?Sign error: (.*)$")),
m_expectSnippet(false)
{
setObjectName(QLatin1String("ClangParser"));
appendOutputParser(new LdParser);
}
void ClangParser::stdError(const QString &line)
{
const QString lne = rightTrimmed(line);
if (m_summaryRegExp.indexIn(lne) > -1) {
doFlush();
m_expectSnippet = false;
return;
}
if (m_commandRegExp.indexIn(lne) > -1) {
m_expectSnippet = true;
Task task(Task::Error,
m_commandRegExp.cap(4),
Utils::FileName(), /* filename */
-1, /* line */
Constants::TASK_CATEGORY_COMPILE);
if (m_commandRegExp.cap(3) == QLatin1String("warning"))
task.type = Task::Warning;
else if (m_commandRegExp.cap(3) == QLatin1String("note"))
task.type = Task::Unknown;
newTask(task);
return;
}
if (m_inLineRegExp.indexIn(lne) > -1) {
m_expectSnippet = true;
newTask(Task(Task::Unknown,
lne.trimmed(),
Utils::FileName::fromUserInput(m_inLineRegExp.cap(2)), /* filename */
m_inLineRegExp.cap(3).toInt(), /* line */
Constants::TASK_CATEGORY_COMPILE));
return;
}
if (m_messageRegExp.indexIn(lne) > -1) {
m_expectSnippet = true;
bool ok = false;
int lineNo = m_messageRegExp.cap(4).toInt(&ok);
if (!ok)
lineNo = m_messageRegExp.cap(5).toInt(&ok);
Task task(Task::Error,
m_messageRegExp.cap(8),
Utils::FileName::fromUserInput(m_messageRegExp.cap(1)), /* filename */
lineNo,
Core::Id(Constants::TASK_CATEGORY_COMPILE));
if (m_messageRegExp.cap(7) == QLatin1String("warning"))
task.type = Task::Warning;
else if (m_messageRegExp.cap(7) == QLatin1String("note"))
task.type = Task::Unknown;
newTask(task);
return;
}
if (m_codesignRegExp.indexIn(lne) > -1) {
m_expectSnippet = true;
Task task(Task::Error,
m_codesignRegExp.cap(1),
Utils::FileName(),
-1,
Core::Id(Constants::TASK_CATEGORY_COMPILE));
newTask(task);
return;
}
if (m_expectSnippet) {
amendDescription(lne, true);
return;
}
IOutputParser::stdError(line);
}
// Unit tests:
#ifdef WITH_TESTS_NOTDISABLED
# include <QTest>
# include "projectexplorer.h"
# include "metatypedeclarations.h"
# include "outputparser_test.h"
void ProjectExplorerPlugin::testClangOutputParser_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<OutputParserTester::Channel>("inputChannel");
QTest::addColumn<QString>("childStdOutLines");
QTest::addColumn<QString>("childStdErrLines");
QTest::addColumn<QList<ProjectExplorer::Task> >("tasks");
QTest::addColumn<QString>("outputLines");
const Core::Id categoryCompile = Constants::TASK_CATEGORY_COMPILE;
QTest::newRow("pass-through stdout")
<< QString::fromLatin1("Sometext") << OutputParserTester::STDOUT
<< QString::fromLatin1("Sometext\n") << QString()
<< QList<ProjectExplorer::Task>()
<< QString();
QTest::newRow("pass-through stderr")
<< QString::fromLatin1("Sometext") << OutputParserTester::STDERR
<< QString() << QString::fromLatin1("Sometext\n")
<< QList<ProjectExplorer::Task>()
<< QString();
QTest::newRow("clang++ warning")
<< QString::fromLatin1("clang++: warning: argument unused during compilation: '-mthreads'")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Warning,
QLatin1String("argument unused during compilation: '-mthreads'"),
Utils::FileName(), -1,
categoryCompile))
<< QString();
QTest::newRow("clang++ error")
<< QString::fromLatin1("clang++: error: no input files [err_drv_no_input_files]")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Error,
QLatin1String("no input files [err_drv_no_input_files]"),
Utils::FileName(), -1,
categoryCompile))
<< QString();
QTest::newRow("complex warning")
<< QString::fromLatin1("In file included from ..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qnamespace.h:45:\n"
"..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qglobal.h(1425) : warning: unknown attribute 'dllimport' ignored [-Wunknown-attributes]\n"
"class Q_CORE_EXPORT QSysInfo {\n"
" ^")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In file included from ..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qnamespace.h:45:"),
Utils::FileName::fromUserInput(QLatin1String("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qnamespace.h")), 45,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("unknown attribute 'dllimport' ignored [-Wunknown-attributes]\n"
"class Q_CORE_EXPORT QSysInfo {\n"
" ^"),
Utils::FileName::fromUserInput(QLatin1String("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qglobal.h")), 1425,
categoryCompile))
<< QString();
QTest::newRow("note")
<< QString::fromLatin1("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qglobal.h:1289:27: note: instantiated from:\n"
"# define Q_CORE_EXPORT Q_DECL_IMPORT\n"
" ^")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("instantiated from:\n"
"# define Q_CORE_EXPORT Q_DECL_IMPORT\n"
" ^"),
Utils::FileName::fromUserInput(QLatin1String("..\\..\\..\\QtSDK1.1\\Desktop\\Qt\\4.7.3\\mingw\\include/QtCore/qglobal.h")), 1289,
categoryCompile))
<< QString();
QTest::newRow("fatal error")
<< QString::fromLatin1("/usr/include/c++/4.6/utility:68:10: fatal error: 'bits/c++config.h' file not found\n"
"#include <bits/c++config.h>\n"
" ^")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Error,
QLatin1String("'bits/c++config.h' file not found\n"
"#include <bits/c++config.h>\n"
" ^"),
Utils::FileName::fromUserInput(QLatin1String("/usr/include/c++/4.6/utility")), 68,
categoryCompile))
<< QString();
QTest::newRow("line confusion")
<< QString::fromLatin1("/home/code/src/creator/src/plugins/coreplugin/manhattanstyle.cpp:567:51: warning: ?: has lower precedence than +; + will be evaluated first [-Wparentheses]\n"
" int x = option->rect.x() + horizontal ? 2 : 6;\n"
" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Warning,
QLatin1String("?: has lower precedence than +; + will be evaluated first [-Wparentheses]\n"
" int x = option->rect.x() + horizontal ? 2 : 6;\n"
" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^"),
Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/coreplugin/manhattanstyle.cpp")), 567,
categoryCompile))
<< QString();
QTest::newRow("code sign error")
<< QString::fromLatin1("Check dependencies\n"
"Code Sign error: No matching provisioning profiles found: No provisioning profiles with a valid signing identity (i.e. certificate and private key pair) were found.\n"
"CodeSign error: code signing is required for product type 'Application' in SDK 'iOS 7.0'")
<< OutputParserTester::STDERR
<< QString() << QString::fromLatin1("Check dependencies\n")
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Error,
QLatin1String("No matching provisioning profiles found: No provisioning profiles with a valid signing identity (i.e. certificate and private key pair) were found."),
Utils::FileName(), -1,
categoryCompile)
<< Task(Task::Error,
QLatin1String("code signing is required for product type 'Application' in SDK 'iOS 7.0'"),
Utils::FileName(), -1,
categoryCompile))
<< QString();
}
void ProjectExplorerPlugin::testClangOutputParser()
{
OutputParserTester testbench;
testbench.appendOutputParser(new ClangParser);
QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(QList<Task>, tasks);
QFETCH(QString, childStdOutLines);
QFETCH(QString, childStdErrLines);
QFETCH(QString, outputLines);
testbench.testParsing(input, inputChannel,
tasks, childStdOutLines, childStdErrLines,
outputLines);
}
#endif

View File

@@ -0,0 +1,59 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef CLANGPARSER_H
#define CLANGPARSER_H
#include "gccparser.h"
#include <projectexplorer/task.h>
#include <QRegExp>
namespace ProjectExplorer {
class ClangParser : public ProjectExplorer::GccParser
{
Q_OBJECT
public:
ClangParser();
void stdError(const QString &line);
private:
QRegExp m_commandRegExp;
QRegExp m_inLineRegExp;
QRegExp m_messageRegExp;
QRegExp m_summaryRegExp;
QRegExp m_codesignRegExp;
bool m_expectSnippet;
};
} // namespace ProjectExplorer
#endif // CLANGPARSER_H

View File

@@ -0,0 +1,880 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License Version 2.1 by the Free Software
** Foundation and appearing in the file LICENSE.txt included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License Version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "gccparser.h"
#include "ldparser.h"
#include <projectexplorer/task.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/qtcassert.h>
using namespace ProjectExplorer;
// opt. drive letter + filename: (2 brackets)
static const char FILE_PATTERN[] = "(<command[ -]line>|([A-Za-z]:)?[^:]+):";
static const char COMMAND_PATTERN[] = "^(.*[\\\\/])?([a-z0-9]+-[a-z0-9]+-[a-z0-9]+-)?(gcc|g\\+\\+)(-[0-9\\.]+)?(\\.exe)?: ";
GccParser::GccParser()
{
setObjectName(QLatin1String("GCCParser"));
m_regExp.setPattern(QLatin1Char('^') + QLatin1String(FILE_PATTERN)
+ QLatin1String("(\\d+):(\\d+:)?\\s+((fatal |#)?(warning|error|note):?\\s)?([^\\s].+)$"));
m_regExp.setMinimal(true);
QTC_CHECK(m_regExp.isValid());
m_regExpIncluded.setPattern(QString::fromLatin1("\\bfrom\\s") + QLatin1String(FILE_PATTERN)
+ QLatin1String("(\\d+)(:\\d+)?[,:]?$"));
m_regExpIncluded.setMinimal(true);
QTC_CHECK(m_regExpIncluded.isValid());
// optional path with trailing slash
// optional arm-linux-none-thingy
// name of executable
// optional trailing version number
// optional .exe postfix
m_regExpGccNames.setPattern(QLatin1String(COMMAND_PATTERN));
m_regExpGccNames.setMinimal(true);
QTC_CHECK(m_regExpGccNames.isValid());
appendOutputParser(new LdParser);
}
void GccParser::stdError(const QString &line)
{
QString lne = rightTrimmed(line);
// Blacklist some lines to not handle them:
if (lne.startsWith(QLatin1String("TeamBuilder ")) ||
lne.startsWith(QLatin1String("distcc["))) {
IOutputParser::stdError(line);
return;
}
// Handle misc issues:
if (lne.startsWith(QLatin1String("ERROR:")) ||
lne == QLatin1String("* cpp failed")) {
newTask(Task(Task::Error,
lne /* description */,
Utils::FileName() /* filename */,
-1 /* linenumber */,
Constants::TASK_CATEGORY_COMPILE));
return;
} else if (m_regExpGccNames.indexIn(lne) > -1) {
QString description = lne.mid(m_regExpGccNames.matchedLength());
Task task(Task::Error,
description,
Utils::FileName(), /* filename */
-1, /* line */
Constants::TASK_CATEGORY_COMPILE);
if (description.startsWith(QLatin1String("warning: "))) {
task.type = Task::Warning;
task.description = description.mid(9);
} else if (description.startsWith(QLatin1String("fatal: "))) {
task.description = description.mid(7);
}
newTask(task);
return;
} else if (m_regExp.indexIn(lne) > -1) {
Utils::FileName filename = Utils::FileName::fromUserInput(m_regExp.cap(1));
int lineno = m_regExp.cap(3).toInt();
Task task(Task::Unknown,
m_regExp.cap(8) /* description */,
filename, lineno,
Constants::TASK_CATEGORY_COMPILE);
if (m_regExp.cap(7) == QLatin1String("warning"))
task.type = Task::Warning;
else if (m_regExp.cap(7) == QLatin1String("error") ||
task.description.startsWith(QLatin1String("undefined reference to")) ||
task.description.startsWith(QLatin1String("multiple definition of")))
task.type = Task::Error;
// Prepend "#warning" or "#error" if that triggered the match on (warning|error)
// We want those to show how the warning was triggered
if (m_regExp.cap(5).startsWith(QLatin1Char('#')))
task.description = m_regExp.cap(5) + task.description;
newTask(task);
return;
} else if (m_regExpIncluded.indexIn(lne) > -1) {
newTask(Task(Task::Unknown,
lne.trimmed() /* description */,
Utils::FileName::fromUserInput(m_regExpIncluded.cap(1)) /* filename */,
m_regExpIncluded.cap(3).toInt() /* linenumber */,
Constants::TASK_CATEGORY_COMPILE));
return;
} else if (lne.startsWith(QLatin1Char(' '))) {
amendDescription(lne, true);
return;
}
doFlush();
IOutputParser::stdError(line);
}
void GccParser::stdOutput(const QString &line)
{
doFlush();
IOutputParser::stdOutput(line);
}
void GccParser::newTask(const Task &task)
{
doFlush();
m_currentTask = task;
}
void GccParser::doFlush()
{
if (m_currentTask.isNull())
return;
Task t = m_currentTask;
m_currentTask.clear();
emit addTask(t);
}
void GccParser::amendDescription(const QString &desc, bool monospaced)
{
if (m_currentTask.isNull())
return;
int start = m_currentTask.description.count() + 1;
m_currentTask.description.append(QLatin1Char('\n'));
m_currentTask.description.append(desc);
if (monospaced) {
QTextLayout::FormatRange fr;
fr.start = start;
fr.length = desc.count() + 1;
fr.format.setFontFamily(QLatin1String("Monospaced"));
fr.format.setFontStyleHint(QFont::TypeWriter);
m_currentTask.formats.append(fr);
}
return;
}
// Unit tests:
#ifdef WITH_TESTS_NOTDISABLED
# include <QTest>
# include "projectexplorer.h"
# include "metatypedeclarations.h"
# include "outputparser_test.h"
void ProjectExplorerPlugin::testGccOutputParsers_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<OutputParserTester::Channel>("inputChannel");
QTest::addColumn<QString>("childStdOutLines");
QTest::addColumn<QString>("childStdErrLines");
QTest::addColumn<QList<ProjectExplorer::Task> >("tasks");
QTest::addColumn<QString>("outputLines");
const Core::Id categoryCompile = Constants::TASK_CATEGORY_COMPILE;
QTest::newRow("pass-through stdout")
<< QString::fromLatin1("Sometext") << OutputParserTester::STDOUT
<< QString::fromLatin1("Sometext\n") << QString()
<< QList<ProjectExplorer::Task>()
<< QString();
QTest::newRow("pass-through stderr")
<< QString::fromLatin1("Sometext") << OutputParserTester::STDERR
<< QString() << QString::fromLatin1("Sometext\n")
<< QList<ProjectExplorer::Task>()
<< QString();
QTest::newRow("ar output")
<< QString::fromLatin1("../../../../x86/i686-unknown-linux-gnu/bin/i686-unknown-linux-gnu-ar: creating lib/libSkyView.a") << OutputParserTester::STDERR
<< QString() << QString::fromLatin1("../../../../x86/i686-unknown-linux-gnu/bin/i686-unknown-linux-gnu-ar: creating lib/libSkyView.a\n")
<< QList<ProjectExplorer::Task>()
<< QString();
QTest::newRow("GCCE error")
<< QString::fromLatin1("/temp/test/untitled8/main.cpp: In function `int main(int, char**)':\n"
"/temp/test/untitled8/main.cpp:9: error: `sfasdf' undeclared (first use this function)\n"
"/temp/test/untitled8/main.cpp:9: error: (Each undeclared identifier is reported only once for each function it appears in.)")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In function `int main(int, char**)':"),
Utils::FileName::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), -1,
categoryCompile)
<< Task(Task::Error,
QLatin1String("`sfasdf' undeclared (first use this function)"),
Utils::FileName::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), 9,
categoryCompile)
<< Task(Task::Error,
QLatin1String("(Each undeclared identifier is reported only once for each function it appears in.)"),
Utils::FileName::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), 9,
categoryCompile)
)
<< QString();
QTest::newRow("GCCE warning")
<< QString::fromLatin1("/src/corelib/global/qglobal.h:1635: warning: inline function `QDebug qDebug()' used but never defined")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Warning,
QLatin1String("inline function `QDebug qDebug()' used but never defined"),
Utils::FileName::fromUserInput(QLatin1String("/src/corelib/global/qglobal.h")), 1635,
categoryCompile))
<< QString();
QTest::newRow("warning")
<< QString::fromLatin1("main.cpp:7:2: warning: Some warning")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>() << Task(Task::Warning,
QLatin1String("Some warning"),
Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 7,
categoryCompile))
<< QString();
QTest::newRow("GCCE #error")
<< QString::fromLatin1("C:\\temp\\test\\untitled8\\main.cpp:7: #error Symbian error")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Error,
QLatin1String("#error Symbian error"),
Utils::FileName::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8\\main.cpp")), 7,
categoryCompile))
<< QString();
// Symbian reports #warning(s) twice (using different syntax).
QTest::newRow("GCCE #warning1")
<< QString::fromLatin1("C:\\temp\\test\\untitled8\\main.cpp:8: warning: #warning Symbian warning")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Warning,
QLatin1String("#warning Symbian warning"),
Utils::FileName::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8\\main.cpp")), 8,
categoryCompile))
<< QString();
QTest::newRow("GCCE #warning2")
<< QString::fromLatin1("/temp/test/untitled8/main.cpp:8:2: warning: #warning Symbian warning")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Warning,
QLatin1String("#warning Symbian warning"),
Utils::FileName::fromUserInput(QLatin1String("/temp/test/untitled8/main.cpp")), 8,
categoryCompile))
<< QString();
QTest::newRow("Undefined reference (debug)")
<< QString::fromLatin1("main.o: In function `main':\n"
"C:\\temp\\test\\untitled8/main.cpp:8: undefined reference to `MainWindow::doSomething()'\n"
"collect2: ld returned 1 exit status")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In function `main':"),
Utils::FileName::fromUserInput(QLatin1String("main.o")), -1,
categoryCompile)
<< Task(Task::Error,
QLatin1String("undefined reference to `MainWindow::doSomething()'"),
Utils::FileName::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8/main.cpp")), 8,
categoryCompile)
<< Task(Task::Error,
QLatin1String("collect2: ld returned 1 exit status"),
Utils::FileName(), -1,
categoryCompile)
)
<< QString();
QTest::newRow("Undefined reference (release)")
<< QString::fromLatin1("main.o: In function `main':\n"
"C:\\temp\\test\\untitled8/main.cpp:(.text+0x40): undefined reference to `MainWindow::doSomething()'\n"
"collect2: ld returned 1 exit status")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In function `main':"),
Utils::FileName::fromUserInput(QLatin1String("main.o")), -1,
categoryCompile)
<< Task(Task::Error,
QLatin1String("undefined reference to `MainWindow::doSomething()'"),
Utils::FileName::fromUserInput(QLatin1String("C:\\temp\\test\\untitled8/main.cpp")), -1,
categoryCompile)
<< Task(Task::Error,
QLatin1String("collect2: ld returned 1 exit status"),
Utils::FileName(), -1,
categoryCompile)
)
<< QString();
QTest::newRow("linker: dll format not recognized")
<< QString::fromLatin1("c:\\Qt\\4.6\\lib/QtGuid4.dll: file not recognized: File format not recognized")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Error,
QLatin1String("file not recognized: File format not recognized"),
Utils::FileName::fromUserInput(QLatin1String("c:\\Qt\\4.6\\lib/QtGuid4.dll")), -1,
categoryCompile))
<< QString();
QTest::newRow("Invalid rpath")
<< QString::fromLatin1("g++: /usr/local/lib: No such file or directory")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Error,
QLatin1String("/usr/local/lib: No such file or directory"),
Utils::FileName(), -1,
categoryCompile))
<< QString();
QTest::newRow("Invalid rpath")
<< QString::fromLatin1("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp: In member function 'void Debugger::Internal::GdbEngine::handleBreakInsert2(const Debugger::Internal::GdbResponse&)':\n"
"../../../../master/src/plugins/debugger/gdb/gdbengine.cpp:2114: warning: unused variable 'index'\n"
"../../../../master/src/plugins/debugger/gdb/gdbengine.cpp:2115: warning: unused variable 'handler'")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In member function 'void Debugger::Internal::GdbEngine::handleBreakInsert2(const Debugger::Internal::GdbResponse&)':"),
Utils::FileName::fromUserInput(QLatin1String("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp")), -1,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("unused variable 'index'"),
Utils::FileName::fromUserInput(QLatin1String("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp")), 2114,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("unused variable 'handler'"),
Utils::FileName::fromUserInput(QLatin1String("../../../../master/src/plugins/debugger/gdb/gdbengine.cpp")), 2115,
categoryCompile))
<< QString();
QTest::newRow("gnumakeparser.cpp errors")
<< QString::fromLatin1("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp: In member function 'void ProjectExplorer::ProjectExplorerPlugin::testGnuMakeParserTaskMangling_data()':\n"
"/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp:264: error: expected primary-expression before ':' token\n"
"/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp:264: error: expected ';' before ':' token")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In member function 'void ProjectExplorer::ProjectExplorerPlugin::testGnuMakeParserTaskMangling_data()':"),
Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp")), -1,
categoryCompile)
<< Task(Task::Error,
QLatin1String("expected primary-expression before ':' token"),
Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp")), 264,
categoryCompile)
<< Task(Task::Error,
QLatin1String("expected ';' before ':' token"),
Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/plugins/projectexplorer/gnumakeparser.cpp")), 264,
categoryCompile))
<< QString();
QTest::newRow("distcc error(QTCREATORBUG-904)")
<< QString::fromLatin1("distcc[73168] (dcc_get_hostlist) Warning: no hostlist is set; can't distribute work\n"
"distcc[73168] (dcc_build_somewhere) Warning: failed to distribute, running locally instead")
<< OutputParserTester::STDERR
<< QString() << QString::fromLatin1("distcc[73168] (dcc_get_hostlist) Warning: no hostlist is set; can't distribute work\n"
"distcc[73168] (dcc_build_somewhere) Warning: failed to distribute, running locally instead\n")
<< QList<ProjectExplorer::Task>()
<< QString();
QTest::newRow("ld warning (QTCREATORBUG-905)")
<< QString::fromLatin1("ld: warning: Core::IEditor* QVariant::value<Core::IEditor*>() const has different visibility (hidden) in .obj/debug-shared/openeditorsview.o and (default) in .obj/debug-shared/editormanager.o")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Warning,
QLatin1String("Core::IEditor* QVariant::value<Core::IEditor*>() const has different visibility (hidden) in .obj/debug-shared/openeditorsview.o and (default) in .obj/debug-shared/editormanager.o"),
Utils::FileName(), -1,
categoryCompile))
<< QString();
QTest::newRow("ld fatal")
<< QString::fromLatin1("ld: fatal: Symbol referencing errors. No output written to testproject")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Error,
QLatin1String("Symbol referencing errors. No output written to testproject"),
Utils::FileName(), -1,
categoryCompile))
<< QString();
QTest::newRow("Teambuilder issues")
<< QString::fromLatin1("TeamBuilder Client:: error: could not find Scheduler, running Job locally...")
<< OutputParserTester::STDERR
<< QString() << QString::fromLatin1("TeamBuilder Client:: error: could not find Scheduler, running Job locally...\n")
<< QList<ProjectExplorer::Task>()
<< QString();
QTest::newRow("note")
<< QString::fromLatin1("/home/dev/creator/share/qtcreator/debugger/dumper.cpp:1079: note: initialized from here")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("initialized from here"),
Utils::FileName::fromUserInput(QLatin1String("/home/dev/creator/share/qtcreator/debugger/dumper.cpp")), 1079,
categoryCompile))
<< QString();
QTest::newRow("static member function")
<< QString::fromLatin1("/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c: In static member function 'static std::_Rb_tree_node_base* std::_Rb_global<_Dummy>::_Rebalance_for_erase(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*&, std::_Rb_tree_node_base*&, std::_Rb_tree_node_base*&)':\n"
"/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c:194: warning: suggest explicit braces to avoid ambiguous 'else'")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In static member function 'static std::_Rb_tree_node_base* std::_Rb_global<_Dummy>::_Rebalance_for_erase(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*&, std::_Rb_tree_node_base*&, std::_Rb_tree_node_base*&)':"),
Utils::FileName::fromUserInput(QLatin1String("/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c")), -1,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("suggest explicit braces to avoid ambiguous 'else'"),
Utils::FileName::fromUserInput(QLatin1String("/Qt/4.6.2-Symbian/s60sdk/epoc32/include/stdapis/stlport/stl/_tree.c")), 194,
categoryCompile))
<< QString();
QTest::newRow("rm false positive")
<< QString::fromLatin1("rm: cannot remove `release/moc_mainwindow.cpp': No such file or directory")
<< OutputParserTester::STDERR
<< QString() << QString(QLatin1String("rm: cannot remove `release/moc_mainwindow.cpp': No such file or directory\n"))
<< QList<ProjectExplorer::Task>()
<< QString();
QTest::newRow("ranlib false positive")
<< QString::fromLatin1("ranlib: file: libSupport.a(HashTable.o) has no symbols")
<< OutputParserTester::STDERR
<< QString() << QString(QLatin1String("ranlib: file: libSupport.a(HashTable.o) has no symbols\n"))
<< QList<ProjectExplorer::Task>()
<< QString();
QTest::newRow("ld: missing library")
<< QString::fromLatin1("/usr/bin/ld: cannot find -ldoesnotexist")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Error,
QLatin1String("cannot find -ldoesnotexist"),
Utils::FileName(), -1,
categoryCompile))
<< QString();
QTest::newRow("In function")
<< QString::fromLatin1("../../scriptbug/main.cpp: In function void foo(i) [with i = double]:\n"
"../../scriptbug/main.cpp:22: instantiated from here\n"
"../../scriptbug/main.cpp:8: warning: unused variable c")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In function void foo(i) [with i = double]:"),
Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), -1,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("instantiated from here"),
Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 22,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("unused variable c"),
Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 8,
categoryCompile))
<< QString();
QTest::newRow("instanciated from here")
<< QString::fromLatin1("main.cpp:10: instantiated from here ")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("instantiated from here"),
Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 10,
categoryCompile))
<< QString();
QTest::newRow("In constructor")
<< QString::fromLatin1("/dev/creator/src/plugins/find/basetextfind.h: In constructor 'Find::BaseTextFind::BaseTextFind(QTextEdit*)':")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In constructor 'Find::BaseTextFind::BaseTextFind(QTextEdit*)':"),
Utils::FileName::fromUserInput(QLatin1String("/dev/creator/src/plugins/find/basetextfind.h")), -1,
categoryCompile))
<< QString();
QTest::newRow("At global scope")
<< QString::fromLatin1("../../scriptbug/main.cpp: At global scope:\n"
"../../scriptbug/main.cpp: In instantiation of void bar(i) [with i = double]:\n"
"../../scriptbug/main.cpp:8: instantiated from void foo(i) [with i = double]\n"
"../../scriptbug/main.cpp:22: instantiated from here\n"
"../../scriptbug/main.cpp:5: warning: unused parameter v")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("At global scope:"),
Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), -1,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("In instantiation of void bar(i) [with i = double]:"),
Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), -1,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("instantiated from void foo(i) [with i = double]"),
Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 8,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("instantiated from here"),
Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 22,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("unused parameter v"),
Utils::FileName::fromUserInput(QLatin1String("../../scriptbug/main.cpp")), 5,
categoryCompile))
<< QString();
QTest::newRow("gcc 4.5 fatal error")
<< QString::fromLatin1("/home/code/test.cpp:54:38: fatal error: test.moc: No such file or directory")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Error,
QLatin1String("test.moc: No such file or directory"),
Utils::FileName::fromUserInput(QLatin1String("/home/code/test.cpp")), 54,
categoryCompile))
<< QString();
QTest::newRow("QTCREATORBUG-597")
<< QString::fromLatin1("debug/qplotaxis.o: In function `QPlotAxis':\n"
"M:\\Development\\x64\\QtPlot/qplotaxis.cpp:26: undefined reference to `vtable for QPlotAxis'\n"
"M:\\Development\\x64\\QtPlot/qplotaxis.cpp:26: undefined reference to `vtable for QPlotAxis'\n"
"collect2: ld returned 1 exit status")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In function `QPlotAxis':"),
Utils::FileName::fromUserInput(QLatin1String("debug/qplotaxis.o")), -1,
categoryCompile)
<< Task(Task::Error,
QLatin1String("undefined reference to `vtable for QPlotAxis'"),
Utils::FileName::fromUserInput(QLatin1String("M:\\Development\\x64\\QtPlot/qplotaxis.cpp")), 26,
categoryCompile)
<< Task(Task::Error,
QLatin1String("undefined reference to `vtable for QPlotAxis'"),
Utils::FileName::fromUserInput(QLatin1String("M:\\Development\\x64\\QtPlot/qplotaxis.cpp")), 26,
categoryCompile)
<< Task(Task::Error,
QLatin1String("collect2: ld returned 1 exit status"),
Utils::FileName(), -1,
categoryCompile))
<< QString();
QTest::newRow("instantiated from here should not be an error")
<< QString::fromLatin1("../stl/main.cpp: In member function typename _Vector_base<_Tp, _Alloc>::_Tp_alloc_type::const_reference Vector<_Tp, _Alloc>::at(int) [with _Tp = Point, _Alloc = Allocator<Point>]:\n"
"../stl/main.cpp:38: instantiated from here\n"
"../stl/main.cpp:31: warning: returning reference to temporary\n"
"../stl/main.cpp: At global scope:\n"
"../stl/main.cpp:31: warning: unused parameter index")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In member function typename _Vector_base<_Tp, _Alloc>::_Tp_alloc_type::const_reference Vector<_Tp, _Alloc>::at(int) [with _Tp = Point, _Alloc = Allocator<Point>]:"),
Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), -1,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("instantiated from here"),
Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), 38,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("returning reference to temporary"),
Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), 31,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("At global scope:"),
Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), -1,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("unused parameter index"),
Utils::FileName::fromUserInput(QLatin1String("../stl/main.cpp")), 31,
categoryCompile))
<< QString();
QTest::newRow("GCCE from lines")
<< QString::fromLatin1("In file included from C:/Symbian_SDK/epoc32/include/e32cmn.h:6792,\n"
" from C:/Symbian_SDK/epoc32/include/e32std.h:25,\n"
"C:/Symbian_SDK/epoc32/include/e32cmn.inl: In member function 'SSecureId::operator const TSecureId&() const':\n"
"C:/Symbian_SDK/epoc32/include/e32cmn.inl:7094: warning: returning reference to temporary")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In file included from C:/Symbian_SDK/epoc32/include/e32cmn.h:6792,"),
Utils::FileName::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32cmn.h")), 6792,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("from C:/Symbian_SDK/epoc32/include/e32std.h:25,"),
Utils::FileName::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32std.h")), 25,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("In member function 'SSecureId::operator const TSecureId&() const':"),
Utils::FileName::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32cmn.inl")), -1,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("returning reference to temporary"),
Utils::FileName::fromUserInput(QLatin1String("C:/Symbian_SDK/epoc32/include/e32cmn.inl")), 7094,
categoryCompile))
<< QString();
QTest::newRow("QTCREATORBUG-2206")
<< QString::fromLatin1("../../../src/XmlUg/targetdelete.c: At top level:")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("At top level:"),
Utils::FileName::fromUserInput(QLatin1String("../../../src/XmlUg/targetdelete.c")), -1,
categoryCompile))
<< QString();
QTest::newRow("GCCE 4: commandline, includes")
<< QString::fromLatin1("In file included from /Symbian/SDK/EPOC32/INCLUDE/GCCE/GCCE.h:15,\n"
" from <command line>:26:\n"
"/Symbian/SDK/epoc32/include/variant/Symbian_OS.hrh:1134:26: warning: no newline at end of file")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In file included from /Symbian/SDK/EPOC32/INCLUDE/GCCE/GCCE.h:15,"),
Utils::FileName::fromUserInput(QLatin1String("/Symbian/SDK/EPOC32/INCLUDE/GCCE/GCCE.h")), 15,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("from <command line>:26:"),
Utils::FileName::fromUserInput(QLatin1String("<command line>")), 26,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("no newline at end of file"),
Utils::FileName::fromUserInput(QLatin1String("/Symbian/SDK/epoc32/include/variant/Symbian_OS.hrh")), 1134,
categoryCompile))
<< QString();
QTest::newRow("Linker fail (release build)")
<< QString::fromLatin1("release/main.o:main.cpp:(.text+0x42): undefined reference to `MainWindow::doSomething()'")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Error,
QLatin1String("undefined reference to `MainWindow::doSomething()'"),
Utils::FileName::fromUserInput(QLatin1String("main.cpp")), -1,
categoryCompile))
<< QString();
QTest::newRow("enumeration warning")
<< QString::fromLatin1("../../../src/shared/proparser/profileevaluator.cpp: In member function 'ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(const ProString&, const ProStringList&)':\n"
"../../../src/shared/proparser/profileevaluator.cpp:2817:9: warning: case value '0' not in enumerated type 'ProFileEvaluator::Private::TestFunc'")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In member function 'ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(const ProString&, const ProStringList&)':"),
Utils::FileName::fromUserInput(QLatin1String("../../../src/shared/proparser/profileevaluator.cpp")), -1,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("case value '0' not in enumerated type 'ProFileEvaluator::Private::TestFunc'"),
Utils::FileName::fromUserInput(QLatin1String("../../../src/shared/proparser/profileevaluator.cpp")), 2817,
categoryCompile))
<< QString();
QTest::newRow("include with line:column info")
<< QString::fromLatin1("In file included from <command-line>:0:0:\n"
"./mw.h:4:0: warning: \"STUPID_DEFINE\" redefined")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In file included from <command-line>:0:0:"),
Utils::FileName::fromUserInput(QLatin1String("<command-line>")), 0,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("\"STUPID_DEFINE\" redefined"),
Utils::FileName::fromUserInput(QLatin1String("./mw.h")), 4,
categoryCompile))
<< QString();
QTest::newRow("instanciation with line:column info")
<< QString::fromLatin1("file.h: In function 'void UnitTest::CheckEqual(UnitTest::TestResults&, const Expected&, const Actual&, const UnitTest::TestDetails&) [with Expected = unsigned int, Actual = int]':\n"
"file.cpp:87:10: instantiated from here\n"
"file.h:21:5: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In function 'void UnitTest::CheckEqual(UnitTest::TestResults&, const Expected&, const Actual&, const UnitTest::TestDetails&) [with Expected = unsigned int, Actual = int]':"),
Utils::FileName::fromUserInput(QLatin1String("file.h")), -1,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("instantiated from here"),
Utils::FileName::fromUserInput(QLatin1String("file.cpp")), 87,
categoryCompile)
<< Task(Task::Warning,
QLatin1String("comparison between signed and unsigned integer expressions [-Wsign-compare]"),
Utils::FileName::fromUserInput(QLatin1String("file.h")), 21,
categoryCompile))
<< QString();
QTest::newRow("linker error") // QTCREATORBUG-3107
<< QString::fromLatin1("cns5k_ins_parser_tests.cpp:(.text._ZN20CNS5kINSParserEngine21DropBytesUntilStartedEP14CircularBufferIhE[CNS5kINSParserEngine::DropBytesUntilStarted(CircularBuffer<unsigned char>*)]+0x6d): undefined reference to `CNS5kINSPacket::SOH_BYTE'")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Error,
QLatin1String("undefined reference to `CNS5kINSPacket::SOH_BYTE'"),
Utils::FileName::fromUserInput(QLatin1String("cns5k_ins_parser_tests.cpp")), -1,
categoryCompile))
<< QString();
QTest::newRow("uic warning")
<< QString::fromLatin1("mainwindow.ui: Warning: The name 'pushButton' (QPushButton) is already in use, defaulting to 'pushButton1'.")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Warning,
QLatin1String("The name 'pushButton' (QPushButton) is already in use, defaulting to 'pushButton1'."),
Utils::FileName::fromUserInput(QLatin1String("mainwindow.ui")), -1,
Constants::TASK_CATEGORY_COMPILE))
<< QString();
QTest::newRow("libimf warning")
<< QString::fromLatin1("libimf.so: warning: warning: feupdateenv is not implemented and will always fail")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Warning,
QLatin1String("warning: feupdateenv is not implemented and will always fail"),
Utils::FileName::fromUserInput(QLatin1String("libimf.so")), -1,
Constants::TASK_CATEGORY_COMPILE))
<< QString();
QTest::newRow("gcc 4.8")
<< QString::fromLatin1("In file included from /home/code/src/creator/src/libs/extensionsystem/pluginerrorview.cpp:31:0:\n"
".uic/ui_pluginerrorview.h:14:25: fatal error: QtGui/QAction: No such file or directory\n"
" #include <QtGui/QAction>\n"
" ^")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In file included from /home/code/src/creator/src/libs/extensionsystem/pluginerrorview.cpp:31:0:"),
Utils::FileName::fromUserInput(QLatin1String("/home/code/src/creator/src/libs/extensionsystem/pluginerrorview.cpp")), 31,
categoryCompile)
<< Task(Task::Error,
QLatin1String("QtGui/QAction: No such file or directory\n"
" #include <QtGui/QAction>\n"
" ^"),
Utils::FileName::fromUserInput(QLatin1String(".uic/ui_pluginerrorview.h")), 14,
categoryCompile))
<< QString();
QTest::newRow("qtcreatorbug-9195")
<< QString::fromLatin1("In file included from /usr/include/qt4/QtCore/QString:1:0,\n"
" from main.cpp:3:\n"
"/usr/include/qt4/QtCore/qstring.h: In function 'void foo()':\n"
"/usr/include/qt4/QtCore/qstring.h:597:5: error: 'QString::QString(const char*)' is private\n"
"main.cpp:7:22: error: within this context")
<< OutputParserTester::STDERR
<< QString() << QString()
<< ( QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In file included from /usr/include/qt4/QtCore/QString:1:0,"),
Utils::FileName::fromUserInput(QLatin1String("/usr/include/qt4/QtCore/QString")), 1,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("from main.cpp:3:"),
Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 3,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("In function 'void foo()':"),
Utils::FileName::fromUserInput(QLatin1String("/usr/include/qt4/QtCore/qstring.h")), -1,
categoryCompile)
<< Task(Task::Error,
QLatin1String("'QString::QString(const char*)' is private"),
Utils::FileName::fromUserInput(QLatin1String("/usr/include/qt4/QtCore/qstring.h")), 597,
categoryCompile)
<< Task(Task::Error,
QLatin1String("within this context"),
Utils::FileName::fromUserInput(QLatin1String("main.cpp")), 7,
categoryCompile))
<< QString();
QTest::newRow("ld: Multiple definition error")
<< QString::fromLatin1("foo.o: In function `foo()':\n"
"/home/user/test/foo.cpp:2: multiple definition of `foo()'\n"
"bar.o:/home/user/test/bar.cpp:4: first defined here\n"
"collect2: error: ld returned 1 exit status")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Unknown,
QLatin1String("In function `foo()':"),
Utils::FileName::fromUserInput(QLatin1String("foo.o")), -1,
categoryCompile)
<< Task(Task::Error,
QLatin1String("multiple definition of `foo()'"),
Utils::FileName::fromUserInput(QLatin1String("/home/user/test/foo.cpp")), 2,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("first defined here"),
Utils::FileName::fromUserInput(QLatin1String("/home/user/test/bar.cpp")), 4,
categoryCompile)
<< Task(Task::Error,
QLatin1String("collect2: error: ld returned 1 exit status"),
Utils::FileName(), -1,
categoryCompile)
)
<< QString();
QTest::newRow("ld: .data section")
<< QString::fromLatin1("foo.o:(.data+0x0): multiple definition of `foo'\n"
"bar.o:(.data+0x0): first defined here\n"
"collect2: error: ld returned 1 exit status")
<< OutputParserTester::STDERR
<< QString() << QString()
<< (QList<ProjectExplorer::Task>()
<< Task(Task::Error,
QLatin1String("multiple definition of `foo'"),
Utils::FileName::fromUserInput(QLatin1String("foo.o")), -1,
categoryCompile)
<< Task(Task::Unknown,
QLatin1String("first defined here"),
Utils::FileName::fromUserInput(QLatin1String("bar.o")), -1,
categoryCompile)
<< Task(Task::Error,
QLatin1String("collect2: error: ld returned 1 exit status"),
Utils::FileName(), -1,
categoryCompile)
)
<< QString();
}
void ProjectExplorerPlugin::testGccOutputParsers()
{
OutputParserTester testbench;
testbench.appendOutputParser(new GccParser);
QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(QList<Task>, tasks);
QFETCH(QString, childStdOutLines);
QFETCH(QString, childStdErrLines);
QFETCH(QString, outputLines);
testbench.testParsing(input, inputChannel,
tasks, childStdOutLines, childStdErrLines,
outputLines);
}
#endif

View File

@@ -0,0 +1,67 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef GCCPARSER_H
#define GCCPARSER_H
#include <projectexplorer/ioutputparser.h>
#include <projectexplorer/task.h>
#include <QRegExp>
namespace ProjectExplorer {
class GccParser : public ProjectExplorer::IOutputParser
{
Q_OBJECT
public:
GccParser();
void stdError(const QString &line);
void stdOutput(const QString &line);
protected:
void newTask(const Task &task);
void doFlush();
void amendDescription(const QString &desc, bool monospaced);
private:
QRegExp m_regExp;
QRegExp m_regExpIncluded;
QRegExp m_regExpGccNames;
Task m_currentTask;
};
} // namespace ProjectExplorer
#endif // GCCPARSER_H

View File

@@ -0,0 +1,126 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License Version 2.1 by the Free Software
** Foundation and appearing in the file LICENSE.txt included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License Version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "ldparser.h"
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/task.h>
#include <utils/qtcassert.h>
using namespace ProjectExplorer;
namespace {
// opt. drive letter + filename: (2 brackets)
const char * const FILE_PATTERN = "(([A-Za-z]:)?[^:]+\\.[^:]+):";
// line no. or elf segment + offset (1 bracket)
// const char * const POSITION_PATTERN = "(\\d+|\\(\\.[^:]+[+-]0x[a-fA-F0-9]+\\):)";
const char * const POSITION_PATTERN = "(\\d+|\\(\\..+[+-]0x[a-fA-F0-9]+\\)):";
const char * const COMMAND_PATTERN = "^(.*[\\\\/])?([a-z0-9]+-[a-z0-9]+-[a-z0-9]+-)?(ld|gold)(-[0-9\\.]+)?(\\.exe)?: ";
}
LdParser::LdParser()
{
setObjectName(QLatin1String("LdParser"));
m_regExpLinker.setPattern(QLatin1Char('^') +
QString::fromLatin1(FILE_PATTERN) + QLatin1Char('(') +
QString::fromLatin1(FILE_PATTERN) + QLatin1String(")?(") +
QLatin1String(POSITION_PATTERN) + QLatin1String(")?\\s(.+)$"));
m_regExpLinker.setMinimal(true);
QTC_CHECK(m_regExpLinker.isValid());
m_regExpGccNames.setPattern(QLatin1String(COMMAND_PATTERN));
m_regExpGccNames.setMinimal(true);
QTC_CHECK(m_regExpGccNames.isValid());
}
void LdParser::stdError(const QString &line)
{
QString lne = rightTrimmed(line);
if (lne.startsWith(QLatin1String("TeamBuilder "))
|| lne.startsWith(QLatin1String("distcc["))
|| lne.contains(QLatin1String("ar: creating "))) {
IOutputParser::stdError(line);
return;
}
if (lne.startsWith(QLatin1String("collect2:"))) {
emit addTask(Task(Task::Error,
lne /* description */,
Utils::FileName() /* filename */,
-1 /* linenumber */,
Constants::TASK_CATEGORY_COMPILE));
return;
} else if (m_regExpGccNames.indexIn(lne) > -1) {
QString description = lne.mid(m_regExpGccNames.matchedLength());
Task task(Task::Error,
description,
Utils::FileName(), /* filename */
-1, /* line */
Constants::TASK_CATEGORY_COMPILE);
if (description.startsWith(QLatin1String("warning: "))) {
task.type = Task::Warning;
task.description = description.mid(9);
} else if (description.startsWith(QLatin1String("fatal: "))) {
task.description = description.mid(7);
}
emit addTask(task);
return;
} else if (m_regExpLinker.indexIn(lne) > -1) {
bool ok;
int lineno = m_regExpLinker.cap(7).toInt(&ok);
if (!ok)
lineno = -1;
Utils::FileName filename = Utils::FileName::fromUserInput(m_regExpLinker.cap(1));
const QString sourceFileName = m_regExpLinker.cap(4);
if (!sourceFileName.isEmpty()
&& !sourceFileName.startsWith(QLatin1String("(.text"))
&& !sourceFileName.startsWith(QLatin1String("(.data"))) {
filename = Utils::FileName::fromUserInput(sourceFileName);
}
QString description = m_regExpLinker.cap(8).trimmed();
Task task(Task::Error, description, filename, lineno,
Constants::TASK_CATEGORY_COMPILE);
if (description.startsWith(QLatin1String("At global scope")) ||
description.startsWith(QLatin1String("At top level")) ||
description.startsWith(QLatin1String("instantiated from ")) ||
description.startsWith(QLatin1String("In ")) ||
description.startsWith(QLatin1String("first defined here"))) {
task.type = Task::Unknown;
}
if (description.startsWith(QLatin1String("warning: "), Qt::CaseInsensitive)) {
task.type = Task::Warning;
task.description = description.mid(9);
}
emit addTask(task);
return;
}
IOutputParser::stdError(line);
}

View File

@@ -0,0 +1,54 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef LDPARSER_H
#define LDPARSER_H
#include <projectexplorer/ioutputparser.h>
#include <QRegExp>
namespace ProjectExplorer {
class LdParser : public ProjectExplorer::IOutputParser
{
Q_OBJECT
public:
LdParser();
void stdError(const QString &line);
private:
QRegExp m_regExpLinker;
QRegExp m_regExpGccNames;
};
} // namespace ProjectExplorer
#endif // GCCPARSER_H

View File

@@ -0,0 +1,232 @@
/****************************************************************************
**
** Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
**
** This file, as part of Qt Creator Plugin for Boost.Build,
** was modified to accommodate OpenProjectWizard requirements.
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License Version 2.1 by the Free Software
** Foundation and appearing in the file LICENSE.txt included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License Version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "filesselectionwizardpage.h"
#include "b2openprojectwizard.h"
#include "b2projectmanagerconstants.h"
#include "selectablefilesmodel.h"
#include <coreplugin/icore.h>
#include <utils/pathchooser.h>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QFileInfo>
namespace BoostBuildProjectManager {
namespace Internal {
FilesSelectionWizardPage::FilesSelectionWizardPage(OpenProjectWizardDialog *openProjectWizard, QWidget *parent)
: QWizardPage(parent), m_openProjectWizardDialog(openProjectWizard), m_model(0), m_finished(false)
{
QVBoxLayout *layout = new QVBoxLayout(this);
createShowFileFilterControls(layout);
createHideFileFilterControls(layout);
createChooseBaseDirControls(layout);
createApplyButton(layout);
m_view = new QTreeView;
m_view->setMinimumSize(500, 400);
m_view->setHeaderHidden(true);
m_view->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
m_label = new QLabel;
m_label->setMaximumWidth(500);
layout->addWidget(m_view);
layout->addWidget(m_label);
}
void FilesSelectionWizardPage::createHideFileFilterControls(QVBoxLayout *layout)
{
QHBoxLayout *hbox = new QHBoxLayout;
m_hideFilesFilterLabel = new QLabel;
m_hideFilesFilterLabel->setText(tr("Hide files matching:"));
m_hideFilesFilterLabel->hide();
hbox->addWidget(m_hideFilesFilterLabel);
m_hideFilesfilterLineEdit = new QLineEdit;
const QString filter = Core::ICore::settings()->value(QLatin1String(Constants::HIDE_FILE_FILTER_SETTING),
QLatin1String(Constants::HIDE_FILE_FILTER_DEFAULT)).toString();
m_hideFilesfilterLineEdit->setText(filter);
m_hideFilesfilterLineEdit->hide();
hbox->addWidget(m_hideFilesfilterLineEdit);
layout->addLayout(hbox);
}
void FilesSelectionWizardPage::createShowFileFilterControls(QVBoxLayout *layout)
{
QHBoxLayout *hbox = new QHBoxLayout;
m_showFilesFilterLabel = new QLabel;
m_showFilesFilterLabel->setText(tr("Show files matching:"));
m_showFilesFilterLabel->hide();
hbox->addWidget(m_showFilesFilterLabel);
m_showFilesfilterLineEdit = new QLineEdit;
const QString filter = Core::ICore::settings()->value(QLatin1String(Constants::SHOW_FILE_FILTER_SETTING),
QLatin1String(Constants::SHOW_FILE_FILTER_DEFAULT)).toString();
m_showFilesfilterLineEdit->setText(filter);
m_showFilesfilterLineEdit->hide();
hbox->addWidget(m_showFilesfilterLineEdit);
layout->addLayout(hbox);
}
void FilesSelectionWizardPage::createChooseBaseDirControls(QVBoxLayout *layout)
{
QHBoxLayout *hbox = new QHBoxLayout;
m_baseDirLabel = new QLabel;
m_baseDirLabel->setText(tr("Base directory:"));
m_baseDirLabel->hide();
hbox->addWidget(m_baseDirLabel);
m_lastBaseDir = m_openProjectWizardDialog->path();
m_baseDirChooser = new Utils::PathChooser;
m_baseDirChooser->setEnabled(true);
m_baseDirChooser->setBaseDirectory(m_lastBaseDir);
m_baseDirChooser->setPath(m_lastBaseDir);
connect(m_baseDirChooser, SIGNAL(changed(QString)), this, SLOT(baseDirectoryChanged()));
hbox->addWidget(m_baseDirChooser);
layout->addLayout(hbox);
}
void FilesSelectionWizardPage::createApplyButton(QVBoxLayout *layout)
{
QHBoxLayout *hbox = new QHBoxLayout;
QSpacerItem *horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
hbox->addItem(horizontalSpacer);
m_applyFilterButton = new QPushButton(tr("Apply Filter"), this);
m_applyFilterButton->hide();
hbox->addWidget(m_applyFilterButton);
layout->addLayout(hbox);
connect(m_applyFilterButton, SIGNAL(clicked()), this, SLOT(applyFilter()));
}
void FilesSelectionWizardPage::initializePage()
{
m_view->setModel(0);
delete m_model;
m_model = new SelectableFilesModel(m_lastBaseDir, this);
connect(m_model, SIGNAL(parsingProgress(QString)),
this, SLOT(parsingProgress(QString)));
connect(m_model, SIGNAL(parsingFinished()),
this, SLOT(parsingFinished()));
m_model->startParsing();
m_hideFilesFilterLabel->setVisible(false);
m_hideFilesfilterLineEdit->setVisible(false);
m_showFilesFilterLabel->setVisible(false);
m_showFilesfilterLineEdit->setVisible(false);
m_applyFilterButton->setVisible(false);
m_view->setVisible(false);
m_label->setVisible(true);
m_view->setModel(m_model);
}
void FilesSelectionWizardPage::cleanupPage()
{
m_model->cancel();
m_model->waitForFinished();
}
void FilesSelectionWizardPage::parsingProgress(const QString &text)
{
m_label->setText(tr("Generating file list...\n\n%1").arg(text));
}
void FilesSelectionWizardPage::parsingFinished()
{
m_finished = true;
m_hideFilesFilterLabel->setVisible(true);
m_hideFilesfilterLineEdit->setVisible(true);
m_showFilesFilterLabel->setVisible(true);
m_showFilesfilterLineEdit->setVisible(true);
m_applyFilterButton->setVisible(true);
m_view->setVisible(true);
m_label->setVisible(false);
m_view->expand(m_view->model()->index(0,0, QModelIndex()));
emit completeChanged();
applyFilter();
// work around qt
m_openProjectWizardDialog->setTitleFormat(m_openProjectWizardDialog->titleFormat());
}
bool FilesSelectionWizardPage::isComplete() const
{
return m_finished;
}
QStringList FilesSelectionWizardPage::selectedPaths() const
{
return m_model ? m_model->selectedPaths() : QStringList();
}
QStringList FilesSelectionWizardPage::selectedFiles() const
{
return m_model ? m_model->selectedFiles() : QStringList();
}
void FilesSelectionWizardPage::applyFilter()
{
const QString showFilesFilter = m_showFilesfilterLineEdit->text();
Core::ICore::settings()->setValue(QLatin1String(Constants::SHOW_FILE_FILTER_SETTING), showFilesFilter);
const QString hideFilesFilter = m_hideFilesfilterLineEdit->text();
Core::ICore::settings()->setValue(QLatin1String(Constants::HIDE_FILE_FILTER_SETTING), hideFilesFilter);
m_model->applyFilter(showFilesFilter, hideFilesFilter);
}
void FilesSelectionWizardPage::baseDirectoryChanged()
{
QString const baseDir(m_baseDirChooser->path());
if (baseDir != m_lastBaseDir && QFileInfo(baseDir).isDir())
{
m_lastBaseDir = baseDir;
initializePage();
}
}
} // namespace Internal
} // namespace BoostBuildProjectManager

View File

@@ -0,0 +1,106 @@
/****************************************************************************
**
** Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
**
** This file, as part of Qt Creator Plugin for Boost.Build,
** was modified to accommodate OpenProjectWizard requirements.
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License Version 2.1 by the Free Software
** Foundation and appearing in the file LICENSE.txt included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License Version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef FILESSELECTIONWIZARDPAGE_HPP
#define FILESSELECTIONWIZARDPAGE_HPP
#include <QWizardPage>
QT_BEGIN_NAMESPACE
class QVBoxLayout;
class QLabel;
class QTreeView;
class QLineEdit;
QT_END_NAMESPACE
namespace Utils {
class PathChooser;
}
namespace BoostBuildProjectManager {
namespace Internal {
class OpenProjectWizardDialog;
class SelectableFilesModel;
// TODO: Submit feature request to Qt Creator to make the FileSelectionWizardPage
// a reusable component.
class FilesSelectionWizardPage : public QWizardPage
{
Q_OBJECT
public:
FilesSelectionWizardPage(OpenProjectWizardDialog *openProjectWizard, QWidget *parent = 0);
bool isComplete() const;
void initializePage();
void cleanupPage();
QStringList selectedFiles() const;
QStringList selectedPaths() const;
private slots:
void applyFilter();
void parsingProgress(const QString &text);
void parsingFinished();
void baseDirectoryChanged();
private:
void createHideFileFilterControls(QVBoxLayout *layout);
void createShowFileFilterControls(QVBoxLayout *layout);
void createChooseBaseDirControls(QVBoxLayout *layout);
void createApplyButton(QVBoxLayout *layout);
OpenProjectWizardDialog *m_openProjectWizardDialog;
SelectableFilesModel *m_model;
QLabel *m_hideFilesFilterLabel;
QLineEdit *m_hideFilesfilterLineEdit;
QLabel *m_showFilesFilterLabel;
QLineEdit *m_showFilesfilterLineEdit;
QPushButton *m_applyFilterButton;
QLabel* m_baseDirLabel;
Utils::PathChooser* m_baseDirChooser;
QString m_lastBaseDir;
QTreeView *m_view;
QLabel *m_label;
bool m_finished;
};
} // namespace Internal
} // namespace BoostBuildProjectManager
#endif // FILESSELECTIONWIZARDPAGE_HPP

View File

@@ -0,0 +1,681 @@
/****************************************************************************
**
** Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
**
** This file, as part of Qt Creator Plugin for Boost.Build,
** was modified to accommodate OpenProjectWizard requirements.
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License Version 2.1 by the Free Software
** Foundation and appearing in the file LICENSE.txt included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License Version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "selectablefilesmodel.h"
#include "b2projectmanagerconstants.h"
#include <coreplugin/fileiconprovider.h>
#include <coreplugin/icore.h>
#include <utils/QtConcurrentTools>
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QTreeView>
#include <QDir>
namespace BoostBuildProjectManager {
namespace Internal {
SelectableFilesModel::SelectableFilesModel(const QString &baseDir, QObject *parent)
: QAbstractItemModel(parent), m_root(0), m_baseDir(baseDir), m_allFiles(true)
{
Q_ASSERT(QFileInfo(m_baseDir).isDir());
// Dummy tree
m_root = new Tree;
m_root->name = QLatin1String("/");
m_root->parent = 0;
m_root->fullPath = m_baseDir;
m_root->isDir = true;
}
void SelectableFilesModel::setInitialMarkedFiles(const QStringList &files)
{
m_files = files.toSet();
m_outOfBaseDirFiles.clear();
QString base = m_baseDir + QLatin1Char('/');
foreach (const QString &file, m_files)
if (!file.startsWith(base))
m_outOfBaseDirFiles.append(file);
m_allFiles = false;
}
void SelectableFilesModel::init()
{
}
void SelectableFilesModel::startParsing()
{
// Build a tree in a future
m_rootForFuture = new Tree;
m_rootForFuture->name = QLatin1String("/");
m_rootForFuture->parent = 0;
m_rootForFuture->fullPath = m_baseDir;
m_rootForFuture->isDir = true;
connect(&m_watcher, SIGNAL(finished()), this, SLOT(buildTreeFinished()));
m_watcher.setFuture(QtConcurrent::run(&SelectableFilesModel::run, this));
}
void SelectableFilesModel::run(QFutureInterface<void> &fi)
{
m_futureCount = 0;
buildTree(m_baseDir, m_rootForFuture, fi);
}
void SelectableFilesModel::buildTreeFinished()
{
beginResetModel();
deleteTree(m_root);
m_root = m_rootForFuture;
m_rootForFuture = 0;
endResetModel();
emit parsingFinished();
}
void SelectableFilesModel::waitForFinished()
{
m_watcher.waitForFinished();
}
void SelectableFilesModel::cancel()
{
m_watcher.cancel();
}
bool SelectableFilesModel::filter(Tree *t)
{
if (t->isDir)
return false;
if (m_files.contains(t->fullPath))
return false;
bool showFilterMatch = false;
//First loop through show file filters and
//if any of them match, cotinue checking.
foreach (const Glob &g, m_showFilesFilter) {
if (g.isMatch(t->name)) {
showFilterMatch = true;
break;
}
}
//If none of the "show file" filters match just return
if (!showFilterMatch)
return true;
foreach (const Glob &g, m_hideFilesFilter) {
if (g.isMatch(t->name))
return true;
}
return false;
}
void SelectableFilesModel::buildTree(const QString &baseDir, Tree *tree, QFutureInterface<void> &fi)
{
const QFileInfoList fileInfoList = QDir(baseDir).entryInfoList(QDir::Files |
QDir::Dirs |
QDir::NoDotAndDotDot);
bool allChecked = true;
bool allUnchecked = true;
foreach (const QFileInfo &fileInfo, fileInfoList) {
if (m_futureCount % 100) {
emit parsingProgress(fileInfo.absoluteFilePath());
if (fi.isCanceled())
return;
}
++m_futureCount;
if (fileInfo.isDir()) {
if (fileInfo.isSymLink())
continue;
Tree *t = new Tree;
t->parent = tree;
t->name = fileInfo.fileName();
t->fullPath = fileInfo.filePath();
t->isDir = true;
buildTree(fileInfo.filePath(), t, fi);
allChecked &= t->checked == Qt::Checked;
allUnchecked &= t->checked == Qt::Unchecked;
tree->childDirectories.append(t);
} else {
Tree *t = new Tree;
t->parent = tree;
t->name = fileInfo.fileName();
t->checked = m_allFiles || m_files.contains(fileInfo.absoluteFilePath()) ? Qt::Checked : Qt::Unchecked;
t->fullPath = fileInfo.filePath();
t->isDir = false;
allChecked &= t->checked == Qt::Checked;
allUnchecked &= t->checked == Qt::Unchecked;
tree->files.append(t);
if (!filter(t))
tree->visibleFiles.append(t);
}
}
if (tree->childDirectories.isEmpty() && tree->visibleFiles.isEmpty())
tree->checked = Qt::Unchecked;
else if (allChecked)
tree->checked = Qt::Checked;
else if (allUnchecked)
tree->checked = Qt::Unchecked;
else
tree->checked = Qt::PartiallyChecked;
}
SelectableFilesModel::~SelectableFilesModel()
{
deleteTree(m_root);
}
void SelectableFilesModel::deleteTree(Tree *tree)
{
foreach (Tree *t, tree->childDirectories)
deleteTree(t);
foreach (Tree *t, tree->files)
deleteTree(t);
delete tree;
}
int SelectableFilesModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 1;
}
int SelectableFilesModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid())
return 1;
Tree *parentT = static_cast<Tree *>(parent.internalPointer());
return parentT->childDirectories.size() + parentT->visibleFiles.size();
}
QModelIndex SelectableFilesModel::index(int row, int column, const QModelIndex &parent) const
{
if (!parent.isValid())
return createIndex(row, column, m_root);
Tree *parentT = static_cast<Tree *>(parent.internalPointer());
if (row < parentT->childDirectories.size())
return createIndex(row, column, parentT->childDirectories.at(row));
else
return createIndex(row, column, parentT->visibleFiles.at(row - parentT->childDirectories.size()));
}
QModelIndex SelectableFilesModel::parent(const QModelIndex &child) const
{
if (!child.isValid())
return QModelIndex();
Tree *parent = static_cast<Tree *>(child.internalPointer())->parent;
if (!parent)
return QModelIndex();
if (!parent->parent) //then the parent is the root
return createIndex(0, 0, parent);
// figure out where the parent is
int pos = parent->parent->childDirectories.indexOf(parent);
if (pos == -1)
pos = parent->parent->childDirectories.size() + parent->parent->visibleFiles.indexOf(parent);
return createIndex(pos, 0, parent);
}
QVariant SelectableFilesModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
Tree *t = static_cast<Tree *>(index.internalPointer());
if (role == Qt::DisplayRole)
return t->name;
if (role == Qt::CheckStateRole)
return t->checked;
if (role == Qt::DecorationRole) {
if (t->icon.isNull())
t->icon = Core::FileIconProvider::icon(t->fullPath);
return t->icon;
}
return QVariant();
}
bool SelectableFilesModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role == Qt::CheckStateRole) {
// We can do that!
Tree *t = static_cast<Tree *>(index.internalPointer());
t->checked = Qt::CheckState(value.toInt());
propagateDown(index);
propagateUp(index);
emit dataChanged(index, index);
}
return false;
}
void SelectableFilesModel::propagateUp(const QModelIndex &index)
{
QModelIndex parent = index.parent();
if (!parent.isValid())
return;
Tree *parentT = static_cast<Tree *>(parent.internalPointer());
if (!parentT)
return;
bool allChecked = true;
bool allUnchecked = true;
for (int i = 0; i < parentT->childDirectories.size(); ++i) {
allChecked &= parentT->childDirectories.at(i)->checked == Qt::Checked;
allUnchecked &= parentT->childDirectories.at(i)->checked == Qt::Unchecked;
}
for (int i = 0; i < parentT->visibleFiles.size(); ++i) {
allChecked &= parentT->visibleFiles.at(i)->checked == Qt::Checked;
allUnchecked &= parentT->visibleFiles.at(i)->checked == Qt::Unchecked;
}
Qt::CheckState newState = Qt::PartiallyChecked;
if (parentT->childDirectories.isEmpty() && parentT->visibleFiles.isEmpty())
newState = Qt::Unchecked;
else if (allChecked)
newState = Qt::Checked;
else if (allUnchecked)
newState = Qt::Unchecked;
if (parentT->checked != newState) {
parentT->checked = newState;
emit dataChanged(parent, parent);
propagateUp(parent);
}
}
void SelectableFilesModel::propagateDown(const QModelIndex &index)
{
Tree *t = static_cast<Tree *>(index.internalPointer());
for (int i = 0; i<t->childDirectories.size(); ++i) {
t->childDirectories[i]->checked = t->checked;
propagateDown(index.child(i, 0));
}
for (int i = 0; i<t->files.size(); ++i)
t->files[i]->checked = t->checked;
int rows = rowCount(index);
if (rows)
emit dataChanged(index.child(0, 0), index.child(rows-1, 0));
}
Qt::ItemFlags SelectableFilesModel::flags(const QModelIndex &index) const
{
Q_UNUSED(index);
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
}
QStringList SelectableFilesModel::selectedPaths() const
{
QStringList result;
collectPaths(m_root, &result);
return result;
}
void SelectableFilesModel::collectPaths(Tree *root, QStringList *result) const
{
if (root->checked == Qt::Unchecked)
return;
result->append(root->fullPath);
foreach (Tree *t, root->childDirectories)
collectPaths(t, result);
}
QStringList SelectableFilesModel::selectedFiles() const
{
QStringList result = m_outOfBaseDirFiles;
collectFiles(m_root, &result);
return result;
}
QStringList SelectableFilesModel::preservedFiles() const
{
return m_outOfBaseDirFiles;
}
void SelectableFilesModel::collectFiles(Tree *root, QStringList *result) const
{
if (root->checked == Qt::Unchecked)
return;
foreach (Tree *t, root->childDirectories)
collectFiles(t, result);
foreach (Tree *t, root->visibleFiles)
if (t->checked == Qt::Checked)
result->append(t->fullPath);
}
QList<Glob> SelectableFilesModel::parseFilter(const QString &filter)
{
QList<Glob> result;
QStringList list = filter.split(QLatin1Char(';'), QString::SkipEmptyParts);
foreach (const QString &e, list) {
QString entry = e.trimmed();
Glob g;
if (entry.indexOf(QLatin1Char('*')) == -1 && entry.indexOf(QLatin1Char('?')) == -1) {
g.mode = Glob::EXACT;
g.matchString = entry;
} else if (entry.startsWith(QLatin1Char('*')) && entry.indexOf(QLatin1Char('*'), 1) == -1
&& entry.indexOf(QLatin1Char('?'), 1) == -1) {
g.mode = Glob::ENDSWITH;
g.matchString = entry.mid(1);
} else {
g.mode = Glob::REGEXP;
g.matchRegexp = QRegExp(entry, Qt::CaseInsensitive, QRegExp::Wildcard);
}
result.append(g);
}
return result;
}
void SelectableFilesModel::applyFilter(const QString &showFilesfilter, const QString &hideFilesfilter)
{
m_showFilesFilter = parseFilter(showFilesfilter);
m_hideFilesFilter = parseFilter(hideFilesfilter);
applyFilter(createIndex(0, 0, m_root));
}
Qt::CheckState SelectableFilesModel::applyFilter(const QModelIndex &index)
{
bool allChecked = true;
bool allUnchecked = true;
Tree *t = static_cast<Tree *>(index.internalPointer());
for (int i=0; i < t->childDirectories.size(); ++i) {
Qt::CheckState childCheckState = applyFilter(index.child(i, 0));
if (childCheckState == Qt::Checked)
allUnchecked = false;
else if (childCheckState == Qt::Unchecked)
allChecked = false;
else
allChecked = allUnchecked = false;
}
int visibleIndex = 0;
int visibleEnd = t->visibleFiles.size();
int startOfBlock = 0;
bool removeBlock = false;
// first remove filtered out rows..
for (;visibleIndex < visibleEnd; ++visibleIndex) {
if (startOfBlock == visibleIndex) {
removeBlock = filter(t->visibleFiles.at(visibleIndex));
} else if (removeBlock != filter(t->visibleFiles.at(visibleIndex))) {
if (removeBlock) {
beginRemoveRows(index, startOfBlock, visibleIndex - 1);
for (int i=startOfBlock; i < visibleIndex; ++i)
t->visibleFiles[i]->checked = Qt::Unchecked;
t->visibleFiles.erase(t->visibleFiles.begin() + startOfBlock,
t->visibleFiles.begin() + visibleIndex);
endRemoveRows();
visibleIndex = startOfBlock; // start again at startOfBlock
visibleEnd = t->visibleFiles.size();
}
removeBlock = filter(t->visibleFiles.at(visibleIndex));
startOfBlock = visibleIndex;
}
}
if (removeBlock) {
beginRemoveRows(index, startOfBlock, visibleEnd - 1);
for (int i=startOfBlock; i < visibleEnd; ++i)
t->visibleFiles[i]->checked = Qt::Unchecked;
t->visibleFiles.erase(t->visibleFiles.begin() + startOfBlock,
t->visibleFiles.begin() + visibleEnd);
endRemoveRows();
}
// Figure out which rows should be visible
QList<Tree *> newRows;
for (int i=0; i < t->files.size(); ++i)
if (!filter(t->files.at(i)))
newRows.append(t->files.at(i));
// now add them!
startOfBlock = 0;
visibleIndex = 0;
visibleEnd = t->visibleFiles.size();
int newIndex = 0;
int newEnd = newRows.size();
while (true) {
while (visibleIndex < visibleEnd && newIndex < newEnd &&
t->visibleFiles.at(visibleIndex) == newRows.at(newIndex)) {
++newIndex;
++visibleIndex;
}
if (visibleIndex >= visibleEnd || newIndex >= newEnd)
break;
startOfBlock = newIndex;
while (newIndex < newEnd &&
t->visibleFiles.at(visibleIndex) != newRows.at(newIndex)) {
++newIndex;
}
// end of block = newIndex
beginInsertRows(index, visibleIndex, visibleIndex + newIndex-startOfBlock-1);
for (int i= newIndex - 1; i >= startOfBlock; --i)
t->visibleFiles.insert(visibleIndex, newRows.at(i));
endInsertRows();
visibleIndex = visibleIndex + newIndex-startOfBlock;
visibleEnd = visibleEnd + newIndex-startOfBlock;
if (newIndex >= newEnd)
break;
}
if (newIndex != newEnd) {
beginInsertRows(index, visibleIndex, visibleIndex + newEnd-newIndex-1);
for (int i=newEnd-1; i >=newIndex; --i)
t->visibleFiles.insert(visibleIndex, newRows.at(i));
endInsertRows();
}
for (int i=0; i < t->visibleFiles.size(); ++i) {
if (t->visibleFiles.at(i)->checked == Qt::Checked)
allUnchecked = false;
else
allChecked = false;
}
Qt::CheckState newState = Qt::PartiallyChecked;
if (t->childDirectories.isEmpty() && t->visibleFiles.isEmpty())
newState = Qt::Unchecked;
else if (allChecked)
newState = Qt::Checked;
else if (allUnchecked)
newState = Qt::Unchecked;
if (t->checked != newState) {
t->checked = newState;
emit dataChanged(index, index);
}
return newState;
}
//////////
// SelectableFilesDialog
//////////
SelectableFilesDialog::SelectableFilesDialog(const QString &path, const QStringList files, QWidget *parent)
: QDialog(parent)
{
QVBoxLayout *layout = new QVBoxLayout();
setLayout(layout);
setWindowTitle(tr("Edit Files"));
m_view = new QTreeView(this);
createShowFileFilterControls(layout);
createHideFileFilterControls(layout);
createApplyButton(layout);
m_selectableFilesModel = new SelectableFilesModel(path, this);
m_selectableFilesModel->setInitialMarkedFiles(files);
m_view->setModel(m_selectableFilesModel);
m_view->setMinimumSize(500, 400);
m_view->setHeaderHidden(true);
m_view->hide();
layout->addWidget(m_view);
m_preservedFiles = new QLabel;
m_preservedFiles->hide();
layout->addWidget(m_preservedFiles);
m_progressLabel = new QLabel(this);
m_progressLabel->setMaximumWidth(500);
layout->addWidget(m_progressLabel);
QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal, this);
buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()),
this, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()),
this, SLOT(reject()));
layout->addWidget(buttonBox);
connect(m_selectableFilesModel, SIGNAL(parsingProgress(QString)),
this, SLOT(parsingProgress(QString)));
connect(m_selectableFilesModel, SIGNAL(parsingFinished()),
this, SLOT(parsingFinished()));
m_selectableFilesModel->startParsing();
}
void SelectableFilesDialog::createHideFileFilterControls(QVBoxLayout *layout)
{
QHBoxLayout *hbox = new QHBoxLayout;
m_hideFilesFilterLabel = new QLabel;
m_hideFilesFilterLabel->setText(tr("Hide files matching:"));
m_hideFilesFilterLabel->hide();
hbox->addWidget(m_hideFilesFilterLabel);
m_hideFilesfilterLineEdit = new QLineEdit;
const QString filter = Core::ICore::settings()->value(QLatin1String(Constants::HIDE_FILE_FILTER_SETTING),
QLatin1String(Constants::HIDE_FILE_FILTER_DEFAULT)).toString();
m_hideFilesfilterLineEdit->setText(filter);
m_hideFilesfilterLineEdit->hide();
hbox->addWidget(m_hideFilesfilterLineEdit);
layout->addLayout(hbox);
}
void SelectableFilesDialog::createShowFileFilterControls(QVBoxLayout *layout)
{
QHBoxLayout *hbox = new QHBoxLayout;
m_showFilesFilterLabel = new QLabel;
m_showFilesFilterLabel->setText(tr("Show files matching:"));
m_showFilesFilterLabel->hide();
hbox->addWidget(m_showFilesFilterLabel);
m_showFilesfilterLineEdit = new QLineEdit;
const QString filter = Core::ICore::settings()->value(QLatin1String(Constants::SHOW_FILE_FILTER_SETTING),
QLatin1String(Constants::SHOW_FILE_FILTER_DEFAULT)).toString();
m_showFilesfilterLineEdit->setText(filter);
m_showFilesfilterLineEdit->hide();
hbox->addWidget(m_showFilesfilterLineEdit);
layout->addLayout(hbox);
}
void SelectableFilesDialog::createApplyButton(QVBoxLayout *layout)
{
QHBoxLayout *hbox = new QHBoxLayout;
QSpacerItem *horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
hbox->addItem(horizontalSpacer);
m_applyFilterButton = new QPushButton(tr("Apply Filter"), this);
m_applyFilterButton->hide();
hbox->addWidget(m_applyFilterButton);
layout->addLayout(hbox);
connect(m_applyFilterButton, SIGNAL(clicked()), this, SLOT(applyFilter()));
}
SelectableFilesDialog::~SelectableFilesDialog()
{
m_selectableFilesModel->cancel();
m_selectableFilesModel->waitForFinished();
}
void SelectableFilesDialog::parsingProgress(const QString &fileName)
{
m_progressLabel->setText(tr("Generating file list...\n\n%1").arg(fileName));
}
void SelectableFilesDialog::parsingFinished()
{
m_hideFilesFilterLabel->show();
m_hideFilesfilterLineEdit->show();
m_showFilesFilterLabel->show();
m_showFilesfilterLineEdit->show();
m_applyFilterButton->show();
m_view->show();
m_progressLabel->hide();
m_view->expand(QModelIndex());
smartExpand(m_selectableFilesModel->index(0,0, QModelIndex()));
applyFilter();
const QStringList &preservedFiles = m_selectableFilesModel->preservedFiles();
if (preservedFiles.isEmpty()) {
m_preservedFiles->hide();
} else {
m_preservedFiles->show();
m_preservedFiles->setText(tr("Not showing %n files that are outside of the base directory.\nThese files are preserved.", 0, preservedFiles.count()));
}
}
void SelectableFilesDialog::smartExpand(const QModelIndex &index)
{
if (m_view->model()->data(index, Qt::CheckStateRole) == Qt::PartiallyChecked) {
m_view->expand(index);
int rows = m_view->model()->rowCount(index);
for (int i = 0; i < rows; ++i)
smartExpand(index.child(i, 0));
}
}
QStringList SelectableFilesDialog::selectedFiles() const
{
return m_selectableFilesModel->selectedFiles();
}
void SelectableFilesDialog::applyFilter()
{
const QString showFilesFilter = m_showFilesfilterLineEdit->text();
Core::ICore::settings()->setValue(QLatin1String(Constants::SHOW_FILE_FILTER_SETTING), showFilesFilter);
const QString hideFilesFilter = m_hideFilesfilterLineEdit->text();
Core::ICore::settings()->setValue(QLatin1String(Constants::HIDE_FILE_FILTER_SETTING), hideFilesFilter);
m_selectableFilesModel->applyFilter(showFilesFilter, hideFilesFilter);
}
} // namespace Internal
} // namespace BoostBuildProjectManager

View File

@@ -0,0 +1,189 @@
/****************************************************************************
**
** Copyright (C) 2013 Mateusz Łoskot <mateusz@loskot.net>
**
** This file, as part of Qt Creator Plugin for Boost.Build,
** was modified to accommodate OpenProjectWizard requirements.
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License Version 2.1 by the Free Software
** Foundation and appearing in the file LICENSE.txt included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License Version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef SELECTABLEFILESMODEL_HPP
#define SELECTABLEFILESMODEL_HPP
#include <QAbstractItemModel>
#include <QSet>
#include <QFutureInterface>
#include <QFutureWatcher>
#include <QDialog>
#include <QTreeView>
#include <QLabel>
QT_BEGIN_NAMESPACE
class QVBoxLayout;
QT_END_NAMESPACE
namespace BoostBuildProjectManager {
namespace Internal {
struct Tree
{
QString name;
Qt::CheckState checked;
bool isDir;
QList<Tree *> childDirectories;
QList<Tree *> files;
QList<Tree *> visibleFiles;
QIcon icon;
QString fullPath;
Tree *parent;
};
struct Glob
{
enum Mode { EXACT, ENDSWITH, REGEXP };
Mode mode;
QString matchString;
mutable QRegExp matchRegexp;
bool isMatch(const QString &text) const
{
if (mode == Glob::EXACT) {
if (text == matchString)
return true;
} else if (mode == Glob::ENDSWITH) {
if (text.endsWith(matchString))
return true;
} else if (mode == Glob::REGEXP) {
if (matchRegexp.exactMatch(text))
return true;
}
return false;
}
};
class SelectableFilesModel : public QAbstractItemModel
{
Q_OBJECT
public:
SelectableFilesModel(const QString &baseDir, QObject *parent);
~SelectableFilesModel();
void setInitialMarkedFiles(const QStringList &files);
int columnCount(const QModelIndex &parent) const;
int rowCount(const QModelIndex &parent) const;
QModelIndex index(int row, int column, const QModelIndex &parent) const;
QModelIndex parent(const QModelIndex &child) const;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QStringList selectedFiles() const;
QStringList selectedPaths() const;
QStringList preservedFiles() const;
// only call this once
void startParsing();
void waitForFinished();
void cancel();
void applyFilter(const QString &selectFilesfilter, const QString &hideFilesfilter);
signals:
void parsingFinished();
void parsingProgress(const QString &filename);
private slots:
void buildTreeFinished();
private:
QList<Glob> parseFilter(const QString &filter);
Qt::CheckState applyFilter(const QModelIndex &index);
bool filter(Tree *t);
void init();
void run(QFutureInterface<void> &fi);
void collectFiles(Tree *root, QStringList *result) const;
void collectPaths(Tree *root, QStringList *result) const;
void buildTree(const QString &baseDir, Tree *tree, QFutureInterface<void> &fi);
void deleteTree(Tree *tree);
void propagateUp(const QModelIndex &index);
void propagateDown(const QModelIndex &index);
Tree *m_root;
// Used in the future thread need to all not used after calling startParsing
QString m_baseDir;
QSet<QString> m_files;
QStringList m_outOfBaseDirFiles;
QFutureWatcher<void> m_watcher;
Tree *m_rootForFuture;
int m_futureCount;
bool m_allFiles;
QList<Glob> m_hideFilesFilter;
QList<Glob> m_showFilesFilter;
};
class SelectableFilesDialog : public QDialog
{
Q_OBJECT
public:
SelectableFilesDialog(const QString &path, const QStringList files, QWidget *parent);
~SelectableFilesDialog();
QStringList selectedFiles() const;
private slots:
void applyFilter();
void parsingProgress(const QString &fileName);
void parsingFinished();
private:
void smartExpand(const QModelIndex &index);
void createShowFileFilterControls(QVBoxLayout *layout);
void createHideFileFilterControls(QVBoxLayout *layout);
void createApplyButton(QVBoxLayout *layout);
SelectableFilesModel *m_selectableFilesModel;
QLabel *m_hideFilesFilterLabel;
QLineEdit *m_hideFilesfilterLineEdit;
QLabel *m_showFilesFilterLabel;
QLineEdit *m_showFilesfilterLineEdit;
QPushButton *m_applyFilterButton;
QTreeView *m_view;
QLabel *m_preservedFiles;
QLabel *m_progressLabel;
};
} // namespace Internal
} // namespace BoostBuildProjectManager
#endif // SELECTABLEFILESMODEL_HPP

View File

@@ -29,6 +29,7 @@ SUBDIRS = \
designer \ designer \
resourceeditor \ resourceeditor \
genericprojectmanager \ genericprojectmanager \
boostbuildprojectmanager \
qmljseditor \ qmljseditor \
qmlprojectmanager \ qmlprojectmanager \
glsleditor \ glsleditor \