aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@digia.com>2012-11-16 14:15:41 +0100
committerChristian Kandeler <christian.kandeler@digia.com>2012-11-27 15:36:27 +0100
commitee7b117b62d220e7ea3f38168ef5e07bd41786d6 (patch)
tree37d952ac2479f929746a0307ff51309dda1b6d22
parent6d6714519cb3e292d52b3ad684201950a01f7c7f (diff)
Make the public API asynchronous.
This simplifies things a lot for IDEs, which are no longer forced to run a dedicated thread for a QbsEngine, as everything on the calling side can now happen in the main loop. Getting rid of blocking operations also allows for more flexibility in that it is now possible to load one project while another is building etc. This patch also moves all the high-level types into src/lib/api/. Until now, they were scattered around language/ and tools/, where they do not belong. Very few things had to be touched in the back-end; most notable is the fact that BuildGraph's ScriptEngine is now settable, because we must exchange it between resolving and building, as the former operation runs in its own thread. Change-Id: I5c8a7cc3517ebc2489678c0d5b1068972997e930 Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
-rw-r--r--src/app/qbs/application.cpp22
-rw-r--r--src/app/qbs/application.h7
-rw-r--r--src/app/qbs/commandlinefrontend.cpp329
-rw-r--r--src/app/qbs/commandlinefrontend.h (renamed from src/lib/language/publicobjectsmap.h)67
-rw-r--r--src/app/qbs/consoleprogressobserver.cpp8
-rw-r--r--src/app/qbs/consoleprogressobserver.h20
-rw-r--r--src/app/qbs/main.cpp237
-rw-r--r--src/app/qbs/qbs.pro6
-rw-r--r--src/app/qbs/showproperties.cpp6
-rw-r--r--src/app/qbs/showproperties.h4
-rw-r--r--src/app/qbs/status.cpp13
-rw-r--r--src/app/qbs/status.h4
-rw-r--r--src/lib/api/api.pri13
-rw-r--r--src/lib/api/internaljobs.cpp303
-rw-r--r--src/lib/api/internaljobs.h172
-rw-r--r--src/lib/api/jobs.cpp224
-rw-r--r--src/lib/api/jobs.h121
-rw-r--r--src/lib/api/project.cpp423
-rw-r--r--src/lib/api/project.h101
-rw-r--r--src/lib/api/projectdata.cpp (renamed from src/lib/language/publictypes.cpp)83
-rw-r--r--src/lib/api/projectdata.h (renamed from src/lib/language/publictypes.h)75
-rw-r--r--src/lib/api/runenvironment.cpp (renamed from src/lib/tools/runenvironment.cpp)18
-rw-r--r--src/lib/api/runenvironment.h (renamed from src/lib/tools/runenvironment.h)10
-rw-r--r--src/lib/buildgraph/buildgraph.cpp23
-rw-r--r--src/lib/buildgraph/buildgraph.h7
-rw-r--r--src/lib/buildgraph/executor.cpp29
-rw-r--r--src/lib/buildgraph/executor.h10
-rw-r--r--src/lib/language/language.pri5
-rw-r--r--src/lib/language/loader.h3
-rw-r--r--src/lib/language/qbsengine.cpp668
-rw-r--r--src/lib/language/qbsengine.h89
-rw-r--r--src/lib/lib.pro7
-rw-r--r--src/lib/qbs.h6
-rw-r--r--src/lib/tools/buildoptions.cpp4
-rw-r--r--src/lib/tools/progressobserver.cpp2
-rw-r--r--src/lib/tools/progressobserver.h2
-rw-r--r--src/lib/tools/tools.pri2
37 files changed, 1893 insertions, 1230 deletions
diff --git a/src/app/qbs/application.cpp b/src/app/qbs/application.cpp
index 9a8842b52..7c83d4f59 100644
--- a/src/app/qbs/application.cpp
+++ b/src/app/qbs/application.cpp
@@ -29,7 +29,7 @@
#include "application.h"
-#include "consoleprogressobserver.h"
+#include "commandlinefrontend.h"
#include "ctrlchandler.h"
#include <logging/logger.h>
#include <logging/translator.h>
@@ -39,10 +39,11 @@ namespace qbs {
static QString cancelMessageTemplate = Tr::tr("Received termination request "
"from user; canceling build. [pid=%1]");
-Application::Application(int &argc, char **argv)
- : QCoreApplication(argc, argv), m_observer(new ConsoleProgressObserver)
+Application::Application(int &argc, char **argv) : QCoreApplication(argc, argv), m_clFrontend(0)
{
- installCtrlCHandler();
+ setApplicationName(QLatin1String("qbs"));
+ setOrganizationName(QLatin1String("QtProject"));
+ setOrganizationDomain(QLatin1String("qt-project.org"));
}
Application *Application::instance()
@@ -50,11 +51,10 @@ Application *Application::instance()
return qobject_cast<Application *>(QCoreApplication::instance());
}
-void Application::init()
+void Application::setCommandLineFrontend(CommandLineFrontend *clFrontend)
{
- setApplicationName(QLatin1String("qbs"));
- setOrganizationName(QLatin1String("QtProject"));
- setOrganizationDomain(QLatin1String("qt-project.org"));
+ installCtrlCHandler();
+ m_clFrontend = clFrontend;
}
/**
@@ -62,11 +62,9 @@ void Application::init()
*/
void Application::userInterrupt()
{
- if (!m_observer)
- return;
-
qbsInfo() << cancelMessageTemplate.arg(applicationPid());
- m_observer->setCanceled(true);
+ Q_ASSERT(m_clFrontend);
+ m_clFrontend->cancel();
}
} // namespace qbs
diff --git a/src/app/qbs/application.h b/src/app/qbs/application.h
index 19a617599..5975266d9 100644
--- a/src/app/qbs/application.h
+++ b/src/app/qbs/application.h
@@ -33,7 +33,7 @@
#include <QCoreApplication>
namespace qbs {
-class ConsoleProgressObserver;
+class CommandLineFrontend;
class Application : public QCoreApplication
{
@@ -43,12 +43,11 @@ public:
static Application *instance();
- void init();
+ void setCommandLineFrontend(CommandLineFrontend *clFrontend);
void userInterrupt();
- ConsoleProgressObserver *observer() const { return m_observer; }
private:
- ConsoleProgressObserver * const m_observer;
+ CommandLineFrontend *m_clFrontend;
};
} // namespace qbs
diff --git a/src/app/qbs/commandlinefrontend.cpp b/src/app/qbs/commandlinefrontend.cpp
new file mode 100644
index 000000000..d8648e8b1
--- /dev/null
+++ b/src/app/qbs/commandlinefrontend.cpp
@@ -0,0 +1,329 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "commandlinefrontend.h"
+
+#include "application.h"
+#include "consoleprogressobserver.h"
+#include "showproperties.h"
+#include "status.h"
+
+#include <qbs.h>
+#include <api/runenvironment.h>
+
+#include <QDir>
+#include <QProcessEnvironment>
+#include <cstdlib>
+
+namespace qbs {
+
+CommandLineFrontend::CommandLineFrontend(const CommandLineParser &parser, QObject *parent)
+ : QObject(parent), m_parser(parser), m_observer(0)
+{
+}
+
+void CommandLineFrontend::cancel()
+{
+ foreach (AbstractJob * const job, m_resolveJobs)
+ job->cancel();
+ foreach (AbstractJob * const job, m_buildJobs)
+ job->cancel();
+}
+
+void CommandLineFrontend::start()
+{
+ try {
+ if (m_parser.showProgress())
+ m_observer = new ConsoleProgressObserver;
+ foreach (const QVariantMap &buildConfig, m_parser.buildConfigurations()) {
+ SetupProjectJob * const job = Project::setupProject(m_parser.projectFileName(),
+ buildConfig, QDir::currentPath(), this);
+ connectJob(job);
+ m_resolveJobs << job;
+ }
+
+ /*
+ * Progress reporting on the terminal gets a bit tricky when resolving several projects
+ * concurrently, since we cannot show multiple progress bars at the same time. Instead,
+ * we just set the total effort to the number of projects and increase the progress
+ * every time one of them finishes, ingoring the progress reports from the jobs themselves.
+ * (Yes, that does mean it will take disproportionately long for the first progress
+ * notification to arrive.)
+ */
+ if (m_parser.showProgress() && resolvingMultipleProjects())
+ m_observer->initialize(tr("Setting up projects"), m_resolveJobs.count());
+ } catch (const Error &error) {
+ qbsError() << error.toString();
+ if (m_buildJobs.isEmpty() && m_resolveJobs.isEmpty())
+ qApp->exit(EXIT_FAILURE);
+ else
+ cancel();
+ }
+}
+
+void CommandLineFrontend::handleJobFinished(bool success, AbstractJob *job)
+{
+ job->deleteLater();
+ if (!success) {
+ qbsError() << job->error().toString();
+ m_resolveJobs.removeOne(job);
+ m_buildJobs.removeOne(job);
+ if (m_resolveJobs.isEmpty() && m_buildJobs.isEmpty()) {
+ qApp->exit(EXIT_FAILURE);
+ return;
+ }
+ cancel();
+ } else if (SetupProjectJob * const setupJob = qobject_cast<SetupProjectJob *>(job)) {
+ m_resolveJobs.removeOne(job);
+ m_projects << setupJob->project();
+ if (m_observer && resolvingMultipleProjects())
+ m_observer->incrementProgressValue();
+ if (m_resolveJobs.isEmpty())
+ handleProjectsResolved();
+ } else { // Build or clean.
+ m_buildJobs.removeOne(job);
+ if (m_buildJobs.isEmpty()) {
+ if (m_parser.command() == CommandLineParser::RunCommand)
+ qApp->exit(runTarget());
+ else
+ qApp->quit();
+ }
+ }
+}
+
+void CommandLineFrontend::handleNewTaskStarted(const QString &description, int totalEffort)
+{
+ if (isBuilding()) {
+ m_totalBuildEffort += totalEffort;
+ if (++m_buildEffortsRetrieved == m_buildEffortsNeeded) {
+ m_observer->initialize(tr("Building"), m_totalBuildEffort);
+ if (m_currentBuildEffort > 0)
+ m_observer->setProgressValue(m_currentBuildEffort);
+ }
+ } else if (!resolvingMultipleProjects()) {
+ m_observer->initialize(description, totalEffort);
+ }
+}
+
+void CommandLineFrontend::handleTaskProgress(int value, AbstractJob *job)
+{
+ if (isBuilding()) {
+ int &currentJobEffort = m_buildEfforts[job];
+ m_currentBuildEffort += value - currentJobEffort;
+ currentJobEffort = value;
+ if (m_buildEffortsRetrieved == m_buildEffortsNeeded)
+ m_observer->setProgressValue(m_currentBuildEffort);
+ } else if (!resolvingMultipleProjects()) {
+ m_observer->setProgressValue(value);
+ }
+}
+
+bool CommandLineFrontend::resolvingMultipleProjects() const
+{
+ return isResolving() && m_resolveJobs.count() + m_projects.count() > 1;
+}
+
+bool CommandLineFrontend::isResolving() const
+{
+ return !m_resolveJobs.isEmpty();
+}
+
+bool CommandLineFrontend::isBuilding() const
+{
+ return !m_buildJobs.isEmpty();
+}
+
+CommandLineFrontend::ProductMap CommandLineFrontend::productsToUse() const
+{
+ ProductMap products;
+ QStringList productNames;
+ const bool useAll = m_parser.products().isEmpty();
+ foreach (const Project &project, m_projects) {
+ QList<ProductData> &productList = products[project];
+ const ProjectData projectData = project.projectData();
+ foreach (const ProductData &product, projectData.products()) {
+ if (useAll || m_parser.products().contains(product.name())) {
+ productList << product;
+ productNames << product.name();
+ }
+ }
+ }
+
+ foreach (const QString &productName, m_parser.products()) {
+ if (!productNames.contains(productName)) {
+ qbsWarning() << QCoreApplication::translate("qbs", "No such product '%1'.")
+ .arg(productName);
+ }
+ }
+
+ return products;
+}
+
+void CommandLineFrontend::handleProjectsResolved()
+{
+ try {
+ switch (m_parser.command()) {
+ case CommandLineParser::CleanCommand:
+ makeClean();
+ break;
+ case CommandLineParser::StartShellCommand:
+ qApp->exit(runShell());
+ break;
+ case CommandLineParser::StatusCommand: {
+ QList<ProjectData> projects;
+ foreach (const Project &project, m_projects)
+ projects << project.projectData();
+ qApp->exit(printStatus(projects));
+ break;
+ }
+ case CommandLineParser::PropertiesCommand: {
+ QList<ProductData> products;
+ const ProductMap &p = productsToUse();
+ foreach (const QList<ProductData> &pProducts, p)
+ products << pProducts;
+ qApp->exit(showProperties(products));
+ break;
+ }
+ case CommandLineParser::BuildCommand:
+ case CommandLineParser::RunCommand:
+ build();
+ break;
+ }
+ } catch (const Error &error) {
+ qbsError() << error.toString();
+ qApp->exit(EXIT_FAILURE);
+ }
+}
+
+void CommandLineFrontend::makeClean()
+{
+ if (m_parser.products().isEmpty()) {
+ foreach (const Project &project, m_projects) {
+ m_buildJobs << project.cleanAllProducts(m_parser.buildOptions(),
+ Project::CleanupTemporaries, this);
+ }
+ } else {
+ const ProductMap &products = productsToUse();
+ for (ProductMap::ConstIterator it = products.begin(); it != products.end(); ++it) {
+ m_buildJobs << it.key().cleanSomeProducts(it.value(), m_parser.buildOptions(),
+ Project::CleanupTemporaries, this);
+
+ }
+ }
+ connectBuildJobs();
+}
+
+int CommandLineFrontend::runShell()
+{
+ // TODO: Don't take a random product.
+ const Project &project = m_projects.first();
+ RunEnvironment runEnvironment = project.getRunEnvironment(project.projectData().products().first(),
+ QProcessEnvironment::systemEnvironment());
+ return runEnvironment.runShell();
+}
+
+void CommandLineFrontend::build()
+{
+ if (m_parser.products().isEmpty()) {
+ foreach (const Project &project, m_projects)
+ m_buildJobs << project.buildAllProducts(m_parser.buildOptions(), this);
+ } else {
+ const ProductMap &products = productsToUse();
+ for (ProductMap::ConstIterator it = products.begin(); it != products.end(); ++it)
+ m_buildJobs << it.key().buildSomeProducts(it.value(), m_parser.buildOptions(), this);
+ }
+ connectBuildJobs();
+
+ /*
+ * Progress reporting for the build jobs works as follows: We know that for every job,
+ * the newTaskStarted() signal is emitted exactly once (unless there's an error). So we add up
+ * the respective total efforts as they come in. Once all jobs have reported their total
+ * efforts, we can start the overall progress report.
+ */
+ m_buildEffortsNeeded = m_buildJobs.count();
+ m_buildEffortsRetrieved = 0;
+ m_totalBuildEffort = 0;
+ m_currentBuildEffort = 0;
+}
+
+// TODO: Don't pick a random project
+int CommandLineFrontend::runTarget()
+{
+ ProductData productToRun;
+ QString productFileName;
+
+ const QString targetName = m_parser.runTargetName();
+ const Project &project = m_projects.first();
+ foreach (const ProductData &product, productsToUse().value(project)) {
+ const QString executable = project.targetExecutable(product);
+ if (executable.isEmpty())
+ continue;
+ if (!targetName.isEmpty() && !executable.endsWith(targetName))
+ continue;
+ if (!productFileName.isEmpty()) {
+ qbsError() << tr("There is more than one executable target in "
+ "the project. Please specify which target "
+ "you want to run.");
+ return EXIT_FAILURE;
+ }
+ productFileName = executable;
+ productToRun = product;
+ }
+
+ if (productToRun.name().isEmpty()) {
+ if (targetName.isEmpty())
+ qbsError() << tr("Can't find a suitable product to run.");
+ else
+ qbsError() << tr("No such target: '%1'").arg(targetName);
+ return EXIT_FAILURE;
+ }
+
+ RunEnvironment runEnvironment = project.getRunEnvironment(productToRun,
+ QProcessEnvironment::systemEnvironment());
+ return runEnvironment.runTarget(productFileName, m_parser.runArgs());
+}
+
+void CommandLineFrontend::connectBuildJobs()
+{
+ foreach (AbstractJob * const job, m_buildJobs)
+ connectJob(job);
+}
+
+void CommandLineFrontend::connectJob(AbstractJob *job)
+{
+ connect(job, SIGNAL(finished(bool, qbs::AbstractJob*)),
+ SLOT(handleJobFinished(bool, qbs::AbstractJob*)));
+ if (m_parser.showProgress()) {
+ connect(job, SIGNAL(taskStarted(QString,int,qbs::AbstractJob*)),
+ SLOT(handleNewTaskStarted(QString,int)));
+ connect(job, SIGNAL(taskProgress(int,qbs::AbstractJob*)),
+ SLOT(handleTaskProgress(int,qbs::AbstractJob*)));
+ }
+}
+
+} // namespace qbs
diff --git a/src/lib/language/publicobjectsmap.h b/src/app/qbs/commandlinefrontend.h
index f2b7855a7..558fd6657 100644
--- a/src/lib/language/publicobjectsmap.h
+++ b/src/app/qbs/commandlinefrontend.h
@@ -26,40 +26,63 @@
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
-#ifndef PUBLICOBJECTSMAP_H
-#define PUBLICOBJECTSMAP_H
+#ifndef COMMANDLINEFRONTEND_H
+#define COMMANDLINEFRONTEND_H
-#include "language.h"
-#include "publictypes.h"
+#include "../shared/commandlineparser.h"
+#include <api/project.h>
+#include <api/projectdata.h>
-#include <QtGlobal>
#include <QHash>
+#include <QList>
+#include <QObject>
namespace qbs {
-namespace Internal {
+class AbstractJob;
+class ConsoleProgressObserver;
-class PublicObjectsMap
+class CommandLineFrontend : public QObject
{
+ Q_OBJECT
public:
- void insertGroup(Group::Id id, const ResolvedGroup::Ptr &group) { m_groups.insert(id, group); }
- void insertProduct(Product::Id id, const ResolvedProduct::Ptr &product) {
- m_products.insert(id, product);
- }
- void insertProject(Project::Id id, const ResolvedProject::Ptr &project) {
- m_projects.insert(id, project);
- }
+ explicit CommandLineFrontend(const CommandLineParser &parser, QObject *parent = 0);
- ResolvedGroup::Ptr group(Group::Id id) const { return m_groups.value(id); }
- ResolvedProduct::Ptr product(Product::Id id) const { return m_products.value(id); }
- ResolvedProject::Ptr project(Project::Id id) const { return m_projects.value(id); }
+ void cancel();
+
+private slots:
+ void start();
+ void handleJobFinished(bool success, qbs::AbstractJob *job);
+ void handleNewTaskStarted(const QString &description, int totalEffort);
+ void handleTaskProgress(int value, qbs::AbstractJob *job);
private:
- QHash<Group::Id, ResolvedGroup::Ptr> m_groups;
- QHash<Product::Id, ResolvedProduct::Ptr> m_products;
- QHash<Project::Id, ResolvedProject::Ptr> m_projects;
+ typedef QHash<Project, QList<ProductData> > ProductMap;
+ ProductMap productsToUse() const;
+
+ bool resolvingMultipleProjects() const;
+ bool isResolving() const;
+ bool isBuilding() const;
+ void handleProjectsResolved();
+ void makeClean();
+ int runShell();
+ void build();
+ int runTarget();
+ void connectBuildJobs();
+ void connectJob(AbstractJob *job);
+
+ const CommandLineParser m_parser;
+ QList<AbstractJob *> m_resolveJobs;
+ QList<AbstractJob *> m_buildJobs;
+ QList<Project> m_projects;
+
+ ConsoleProgressObserver *m_observer;
+ int m_buildEffortsNeeded;
+ int m_buildEffortsRetrieved;
+ int m_totalBuildEffort;
+ int m_currentBuildEffort;
+ QHash<AbstractJob *, int> m_buildEfforts;
};
-} // namespace Internal
} // namespace qbs
-#endif // PUBLICOBJECTSMAP_H
+#endif // COMMANDLINEFRONTEND_H
diff --git a/src/app/qbs/consoleprogressobserver.cpp b/src/app/qbs/consoleprogressobserver.cpp
index cebba0d3b..f9f96e35a 100644
--- a/src/app/qbs/consoleprogressobserver.cpp
+++ b/src/app/qbs/consoleprogressobserver.cpp
@@ -35,16 +35,10 @@
namespace qbs {
-ConsoleProgressObserver::ConsoleProgressObserver() : m_showProgress(false), m_canceled(false)
-{
-}
-
void ConsoleProgressObserver::initialize(const QString &task, int max)
{
m_maximum = max;
m_value = 0;
- if (!m_showProgress)
- return;
m_percentage = 0;
m_hashesPrinted = 0;
std::cout << task.toLocal8Bit().constData() << ": 0%" << std::flush;
@@ -55,8 +49,6 @@ void ConsoleProgressObserver::setProgressValue(int value)
if (value > m_maximum || value <= m_value)
return; // TODO: Should be an assertion, but the executor currently breaks it.
m_value = value;
- if (!m_showProgress)
- return;
const int newPercentage = (100 * m_value) / m_maximum;
if (newPercentage == m_percentage)
return;
diff --git a/src/app/qbs/consoleprogressobserver.h b/src/app/qbs/consoleprogressobserver.h
index 8c115a5f5..72de28d53 100644
--- a/src/app/qbs/consoleprogressobserver.h
+++ b/src/app/qbs/consoleprogressobserver.h
@@ -29,25 +29,22 @@
#ifndef CONSOLEPROGRESSOBSERVER_H
#define CONSOLEPROGRESSOBSERVER_H
-#include <tools/progressobserver.h>
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
namespace qbs {
-class ConsoleProgressObserver : public ProgressObserver
+class ConsoleProgressObserver
{
public:
- ConsoleProgressObserver();
-
- void setShowProgress(bool show) { m_showProgress = show; }
- void setCanceled(bool cancel) { m_canceled = cancel; }
-
-private:
void initialize(const QString &task, int max);
void setProgressValue(int value);
- int progressValue() { return m_value; }
- int maximum() const { return m_maximum; }
- bool canceled() const { return m_canceled; }
+ void incrementProgressValue() { setProgressValue(m_value + 1); }
+private:
void eraseCurrentPercentageString();
void updateProgressBarIfNecessary();
void writePercentageString();
@@ -57,7 +54,6 @@ private:
int m_percentage;
int m_hashesPrinted;
bool m_showProgress;
- bool m_canceled;
};
} // namespace qbs
diff --git a/src/app/qbs/main.cpp b/src/app/qbs/main.cpp
index 2c1a91a99..4c95df6d2 100644
--- a/src/app/qbs/main.cpp
+++ b/src/app/qbs/main.cpp
@@ -28,62 +28,18 @@
****************************************************************************/
#include "application.h"
-#include "consoleprogressobserver.h"
-#include "showproperties.h"
-#include "status.h"
+#include "commandlinefrontend.h"
#include "../shared/commandlineparser.h"
#include <qbs.h>
#include <logging/consolelogger.h>
#include <tools/hostosinfo.h>
-#include <tools/runenvironment.h>
-#include <QCoreApplication>
-#include <QDir>
-#include <QObject>
#include <QProcess>
-#include <QProcessEnvironment>
-#include <QScopedPointer>
#include <QTimer>
using namespace qbs;
-enum ExitCode
-{
- ExitCodeOK = 0,
- ExitCodeErrorParsingCommandLine = 1,
- ExitCodeErrorCommandNotImplemented = 2,
- ExitCodeErrorExecutionFailed = 3,
- ExitCodeErrorLoadingProjectFailed = 4,
- ExitCodeErrorBuildFailure = 5
-};
-
-static QList<Product> productsToUse(QbsEngine &qbsEngine, const QList<Project::Id> &projectIds,
- const QStringList &selectedProducts)
-{
- QList<Product> products;
- QStringList productNames;
- const bool useAll = selectedProducts.isEmpty();
- foreach (const Project::Id projectId, projectIds) {
- const Project project = qbsEngine.retrieveProject(projectId);
- foreach (const Product &product, project.products()) {
- if (useAll || selectedProducts.contains(product.name())) {
- products << product;
- productNames << product.name();
- }
- }
- }
-
- foreach (const QString &productName, selectedProducts) {
- if (!productNames.contains(productName)) {
- qbsWarning() << QCoreApplication::translate("qbs", "No such product '%1'.")
- .arg(productName);
- }
- }
-
- return products;
-}
-
static bool tryToRunTool(const QStringList &arguments, int &exitCode)
{
if (arguments.isEmpty())
@@ -98,148 +54,11 @@ static bool tryToRunTool(const QStringList &arguments, int &exitCode)
return exitCode != -2;
}
-static void makeClean(QbsEngine &qbsEngine, const QList<Project::Id> &projectIds,
- const QStringList &productsSetByUser, const BuildOptions &buildOptions)
-{
- const QList<Product> products = productsToUse(qbsEngine, projectIds, productsSetByUser);
- qbsEngine.cleanProducts(products, buildOptions, QbsEngine::CleanupTemporaries);
-}
-
-// TODO: Don't take a random product.
-static int runShell(QbsEngine &qbsEngine, const Project &project)
-{
- try {
- RunEnvironment runEnvironment = qbsEngine.getRunEnvironment(project.products().first(),
- QProcessEnvironment::systemEnvironment());
- return runEnvironment.runShell();
- } catch (const Error &error) {
- qbsError() << error.toString();
- return EXIT_FAILURE;
- }
-}
-
-class AbstractBuilder : public QObject
-{
- Q_OBJECT
-
-protected:
- AbstractBuilder(QbsEngine &qbsEngine, const BuildOptions &buildOptions)
- : m_qbsEngine(qbsEngine), m_buildOptions(buildOptions)
- {
- }
-
- QbsEngine &qbsEngine() const { return m_qbsEngine; }
- const BuildOptions buildOptions() const { return m_buildOptions; }
-
-private slots:
- void build()
- {
- try {
- doBuild();
- qApp->quit();
- } catch (const Error &error) {
- qbsError() << error.toString();
- qApp->exit(ExitCodeErrorBuildFailure);
- }
- }
-
-private:
- virtual void doBuild() = 0;
-
- QbsEngine &m_qbsEngine;
- const BuildOptions m_buildOptions;
-};
-
-class ProductsBuilder : public AbstractBuilder
-{
-public:
- ProductsBuilder(QbsEngine &qbsEngine, const QList<Product> &products,
- const BuildOptions &buildOptions)
- : AbstractBuilder(qbsEngine, buildOptions), m_products(products)
- {
- }
-
-private:
- void doBuild() { qbsEngine().buildProducts(m_products, buildOptions()); }
-
- const QList<Product> m_products;
-};
-
-class ProjectsBuilder : public AbstractBuilder
-{
-public:
- ProjectsBuilder(QbsEngine &qbsEngine, const QList<Project::Id> &projectIds,
- const BuildOptions &buildOptions)
- : AbstractBuilder(qbsEngine, buildOptions), m_projectIds(projectIds)
- {
- }
-
-private:
- void doBuild() { qbsEngine().buildProjects(m_projectIds, buildOptions()); }
-
- const QList<Project::Id> m_projectIds;
-};
-
-static int build(QbsEngine &qbsEngine, const QList<Project::Id> &projectIds,
- const QStringList &productsSetByUser, const BuildOptions &buildOptions)
-{
- QScopedPointer<AbstractBuilder> builder;
- if (productsSetByUser.isEmpty()) {
- builder.reset(new ProjectsBuilder(qbsEngine, projectIds, buildOptions));
- } else {
- const QList<Product> products = productsToUse(qbsEngine, projectIds, productsSetByUser);
- builder.reset(new ProductsBuilder(qbsEngine, products, buildOptions));
- }
- QTimer::singleShot(0, builder.data(), SLOT(build()));
- return qApp->exec();
-}
-
-static int runTarget(QbsEngine &qbsEngine, const QList<Product> &products,
- const QString &targetName, const QStringList &arguments)
-{
- try {
- Product productToRun;
- QString productFileName;
-
- foreach (const Product &product, products) {
- const QString executable = qbsEngine.targetExecutable(product);
- if (executable.isEmpty())
- continue;
- if (!targetName.isEmpty() && !executable.endsWith(targetName))
- continue;
- if (!productFileName.isEmpty()) {
- qbsError() << QObject::tr("There is more than one executable target in "
- "the project. Please specify which target "
- "you want to run.");
- return EXIT_FAILURE;
- }
- productFileName = executable;
- productToRun = product;
- }
-
- if (!productToRun.id().isValid()) {
- if (targetName.isEmpty())
- qbsError() << QObject::tr("Can't find a suitable product to run.");
- else
- qbsError() << QObject::tr("No such target: '%1'").arg(targetName);
- return ExitCodeErrorBuildFailure;
- }
-
- RunEnvironment runEnvironment = qbsEngine.getRunEnvironment(productToRun,
- QProcessEnvironment::systemEnvironment());
- return runEnvironment.runTarget(productFileName, arguments);
- } catch (const Error &error) {
- qbsError() << error.toString();
- return EXIT_FAILURE;
- }
-}
-
int main(int argc, char *argv[])
{
ConsoleLogger cl;
Application app(argc, argv);
- app.init();
QStringList arguments = app.arguments();
arguments.removeFirst();
@@ -250,7 +69,7 @@ int main(int argc, char *argv[])
CommandLineParser parser;
if (!parser.parseCommandLine(arguments)) {
parser.printHelp();
- return ExitCodeErrorParsingCommandLine;
+ return EXIT_FAILURE;
}
if (parser.isHelpSet()) {
@@ -258,52 +77,8 @@ int main(int argc, char *argv[])
return 0;
}
- QList<Project::Id> projectIds;
- QbsEngine qbsEngine;
- if (parser.showProgress())
- app.observer()->setShowProgress(true);
- qbsEngine.setProgressObserver(app.observer());
- try {
- foreach (const QVariantMap &buildConfig, parser.buildConfigurations()) {
- const Project::Id projectId = qbsEngine.setupProject(parser.projectFileName(),
- buildConfig, QDir::currentPath());
- projectIds << projectId;
- }
- } catch (const Error &error) {
- qbsError() << error.toString();
- return ExitCodeErrorLoadingProjectFailed;
- }
-
- try {
- switch (parser.command()) {
- case CommandLineParser::CleanCommand:
- makeClean(qbsEngine, projectIds, parser.products(), parser.buildOptions());
- break;
- case CommandLineParser::StartShellCommand:
- return runShell(qbsEngine, qbsEngine.retrieveProject(projectIds.first()));
- case CommandLineParser::StatusCommand: {
- QList<Project> projects;
- foreach (const Project::Id &id, projectIds)
- projects << qbsEngine.retrieveProject(id);
- return printStatus(projects);
- }
- case CommandLineParser::PropertiesCommand:
- return showProperties(productsToUse(qbsEngine, projectIds, parser.products()));
- case CommandLineParser::BuildCommand:
- return build(qbsEngine, projectIds, parser.products(), parser.buildOptions());
- case CommandLineParser::RunCommand: {
- const int buildExitCode = build(qbsEngine, projectIds, parser.products(),
- parser.buildOptions());
- if (buildExitCode != 0)
- return buildExitCode;
- const QList<Product> products = productsToUse(qbsEngine, projectIds, parser.products());
- return runTarget(qbsEngine, products, parser.runTargetName(), parser.runArgs());
- }
- }
- } catch (const Error &error) {
- qbsError() << error.toString();
- return EXIT_FAILURE;
- }
+ CommandLineFrontend clFrontend(parser);
+ app.setCommandLineFrontend(&clFrontend);
+ QTimer::singleShot(0, &clFrontend, SLOT(start()));
+ return app.exec();
}
-
-#include "main.moc"
diff --git a/src/app/qbs/qbs.pro b/src/app/qbs/qbs.pro
index 039b6b4ec..be7cda590 100644
--- a/src/app/qbs/qbs.pro
+++ b/src/app/qbs/qbs.pro
@@ -12,7 +12,8 @@ SOURCES += main.cpp \
showproperties.cpp \
status.cpp \
../shared/commandlineparser.cpp \
- consoleprogressobserver.cpp
+ consoleprogressobserver.cpp \
+ commandlinefrontend.cpp
HEADERS += \
ctrlchandler.h \
@@ -20,7 +21,8 @@ HEADERS += \
showproperties.h \
status.h \
../shared/commandlineparser.h \
- consoleprogressobserver.h
+ consoleprogressobserver.h \
+ commandlinefrontend.h
include(../../lib/use.pri)
include(../../../qbs_version.pri)
diff --git a/src/app/qbs/showproperties.cpp b/src/app/qbs/showproperties.cpp
index 94c90d6da..0529c77cc 100644
--- a/src/app/qbs/showproperties.cpp
+++ b/src/app/qbs/showproperties.cpp
@@ -69,15 +69,15 @@ static void dumpMap(const QVariantMap &map, const QString &prefix = QString())
}
}
-static void dumpProperties(const Product &product)
+static void dumpProperties(const ProductData &product)
{
printf("--------%s--------\n", qPrintable(product.name()));
dumpMap(product.properties());
}
-int showProperties(const QList<Product> &products)
+int showProperties(const QList<ProductData> &products)
{
- foreach (const Product &product, products)
+ foreach (const ProductData &product, products)
dumpProperties(product);
return 0;
}
diff --git a/src/app/qbs/showproperties.h b/src/app/qbs/showproperties.h
index 1f53bea43..6be329d57 100644
--- a/src/app/qbs/showproperties.h
+++ b/src/app/qbs/showproperties.h
@@ -33,9 +33,9 @@
#include <QList>
namespace qbs {
-class Product;
+class ProductData;
-int showProperties(const QList<Product> &products);
+int showProperties(const QList<ProductData> &products);
} // namespace qbs
diff --git a/src/app/qbs/status.cpp b/src/app/qbs/status.cpp
index 54d0816c7..4a82fd993 100644
--- a/src/app/qbs/status.cpp
+++ b/src/app/qbs/status.cpp
@@ -30,7 +30,6 @@
#include "status.h"
#include <language/language.h>
-
#include <qbs.h>
#include <QDir>
@@ -102,15 +101,15 @@ static QStringList allFilesInProject(const QString &projectRootPath)
return allFilesInDirectoryRecursive(QDir(projectRootPath), ignoreRegularExpressionList);
}
-QStringList allFiles(const Product &product)
+QStringList allFiles(const ProductData &product)
{
QStringList files;
- foreach (const Group &group, product.groups())
+ foreach (const GroupData &group, product.groups())
files += group.allFilePaths();
return files;
}
-int printStatus(const QList<Project> &projects)
+int printStatus(const QList<ProjectData> &projects)
{
if (projects.isEmpty())
return 0;
@@ -120,11 +119,11 @@ int printStatus(const QList<Project> &projects)
QStringList untrackedFilesInProject = allFilesInProject(projectDirectory);
QStringList missingFiles;
- const Project project = projects.first();
- foreach (const Product &product, project.products()) {
+ const ProjectData project = projects.first();
+ foreach (const ProductData &product, project.products()) {
qbsInfo() << DontPrintLogLevel << TextColorBlue << "\nProduct: " << product.name()
<< " (" << product.qbsFilePath() << ":" << product.qbsLine() << ")";
- foreach (const Group &group, product.groups()) {
+ foreach (const GroupData &group, product.groups()) {
qbsInfo() << DontPrintLogLevel << TextColorBlue << " Group: " << group.name()
<< " (" << group.qbsLine() << ")";
QStringList sourceFiles = group.allFilePaths();
diff --git a/src/app/qbs/status.h b/src/app/qbs/status.h
index b92921730..feb281f5e 100644
--- a/src/app/qbs/status.h
+++ b/src/app/qbs/status.h
@@ -37,9 +37,9 @@ class QString;
QT_END_NAMESPACE
namespace qbs {
-class Project;
+class ProjectData;
-int printStatus(const QList<Project> &projects);
+int printStatus(const QList<ProjectData> &projects);
} // namespace qbs
diff --git a/src/lib/api/api.pri b/src/lib/api/api.pri
new file mode 100644
index 000000000..846786ec0
--- /dev/null
+++ b/src/lib/api/api.pri
@@ -0,0 +1,13 @@
+HEADERS += \
+ $$PWD/internaljobs.h \
+ $$PWD/projectdata.h \
+ $$PWD/runenvironment.h \
+ $$PWD/jobs.h \
+ $$PWD/project.h
+
+SOURCES += \
+ $$PWD/internaljobs.cpp \
+ $$PWD/runenvironment.cpp \
+ $$PWD/projectdata.cpp \
+ $$PWD/jobs.cpp \
+ $$PWD/project.cpp
diff --git a/src/lib/api/internaljobs.cpp b/src/lib/api/internaljobs.cpp
new file mode 100644
index 000000000..2b756703d
--- /dev/null
+++ b/src/lib/api/internaljobs.cpp
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "internaljobs.h"
+
+#include <buildgraph/artifactcleaner.h>
+#include <buildgraph/executor.h>
+#include <language/language.h>
+#include <language/loader.h>
+#include <language/scriptengine.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/progressobserver.h>
+#include <tools/settings.h>
+
+#include <QtConcurrentRun>
+#include <QFutureWatcher>
+#include <QMutexLocker>
+#include <QScopedPointer>
+#include <QTimer>
+
+namespace qbs {
+namespace Internal {
+
+class JobObserver : public ProgressObserver
+{
+public:
+ JobObserver(InternalJob *job) : m_canceled(false), m_job(job) { }
+
+ void cancel() { m_canceled = true; }
+
+private:
+ void initialize(const QString &task, int maximum) {
+ m_value = 0;
+ m_maximum = maximum;
+ m_canceled = false;
+ emit m_job->newTaskStarted(task, maximum, m_job);
+ }
+ void setProgressValue(int value) {
+ m_value = value;
+ emit m_job->taskProgress(value, m_job);
+ }
+ int progressValue() { return m_value; }
+ int maximum() const { return m_maximum; }
+ bool canceled() const { return m_canceled; }
+
+ int m_value;
+ int m_maximum;
+ bool m_canceled;
+ InternalJob * const m_job;
+};
+
+
+InternalJob::InternalJob(QObject *parent)
+ : QObject(parent)
+ , m_observer(new JobObserver(this))
+{
+}
+
+void InternalJob::cancel()
+{
+ m_observer->cancel();
+}
+
+
+InternalSetupProjectJob::InternalSetupProjectJob(QObject *parent)
+ : InternalJob(parent), m_running(false)
+{
+}
+
+InternalSetupProjectJob::~InternalSetupProjectJob()
+{
+ QMutexLocker locker(&m_runMutex);
+ while (m_running)
+ m_runWaitCondition.wait(&m_runMutex);
+ }
+
+void InternalSetupProjectJob::resolve(const QString &projectFilePath, const QString &buildRoot,
+ const QVariantMap &buildConfig)
+{
+ m_projectFilePath = projectFilePath;
+ m_buildRoot = buildRoot;
+ m_buildConfig = buildConfig;
+ QTimer::singleShot(0, this, SLOT(start()));
+}
+
+void InternalSetupProjectJob::start()
+{
+ m_running = true;
+ QFutureWatcher<void> * const watcher = new QFutureWatcher<void>(this);
+ connect(watcher, SIGNAL(finished()), SLOT(handleFinished()));
+ watcher->setFuture(QtConcurrent::run(this, &InternalSetupProjectJob::doResolve));
+}
+
+void InternalSetupProjectJob::handleFinished()
+{
+ emit finished(this);
+}
+
+void InternalSetupProjectJob::doResolve()
+{
+ try {
+ execute();
+ } catch (const Error &error) {
+ setError(error.toString());
+ }
+ QMutexLocker locker(&m_runMutex);
+ m_running = false;
+ m_runWaitCondition.wakeOne();
+}
+
+void InternalSetupProjectJob::execute()
+{
+ ScriptEngine scriptEngine;
+ QScopedPointer<BuildGraph> buildGraph(new BuildGraph);
+ buildGraph->setEngine(&scriptEngine);
+ buildGraph->setProgressObserver(observer());
+ const QStringList searchPaths = Settings().searchPaths();
+ const BuildProject::LoadResult loadResult = BuildProject::load(m_projectFilePath,
+ buildGraph.data(), m_buildRoot, m_buildConfig, searchPaths);
+
+ ResolvedProject::Ptr rProject;
+ if (!loadResult.discardLoadedProject)
+ m_buildProject = loadResult.loadedProject;
+ if (m_buildProject) {
+ buildGraph.take();
+ rProject = m_buildProject->resolvedProject();
+ } else {
+ if (loadResult.changedResolvedProject) {
+ rProject = loadResult.changedResolvedProject;
+ } else {
+ Loader loader(&scriptEngine);
+ loader.setSearchPaths(searchPaths);
+ loader.setProgressObserver(observer());
+ rProject = loader.loadProject(m_projectFilePath, m_buildRoot, m_buildConfig);
+ }
+ if (rProject->products.isEmpty())
+ throw Error(Tr::tr("Project '%1' does not contain products.").arg(m_projectFilePath));
+ }
+
+ // copy the environment from the platform config into the project's config
+ const QVariantMap platformEnvironment = m_buildConfig.value("environment").toMap();
+ rProject->platformEnvironment = platformEnvironment;
+
+ qbsDebug("for %s:", qPrintable(rProject->id()));
+ foreach (const ResolvedProduct::ConstPtr &p, rProject->products) {
+ qbsDebug(" - [%s] %s as %s"
+ ,qPrintable(p->fileTags.join(", "))
+ ,qPrintable(p->name)
+ ,qPrintable(p->project->id())
+ );
+ }
+ qbsDebug("");
+
+ if (m_buildProject)
+ return;
+
+ TimedActivityLogger resolveLogger(QLatin1String("Resolving build project"));
+ m_buildProject = buildGraph.data()->resolveProject(rProject);
+ if (loadResult.loadedProject)
+ m_buildProject->rescueDependencies(loadResult.loadedProject);
+ buildGraph.take();
+}
+
+
+BuildGraphTouchingJob::BuildGraphTouchingJob(QObject *parent) : InternalJob(parent)
+{
+}
+
+void BuildGraphTouchingJob::setup(const QList<BuildProduct::Ptr> &products,
+ const BuildOptions &buildOptions)
+{
+ m_products = products;
+ m_buildOptions = buildOptions;
+}
+
+void BuildGraphTouchingJob::storeBuildGraph()
+{
+ try {
+ if (m_buildOptions.dryRun)
+ return;
+ m_products.first()->project->store();
+ } catch (const Error &error) {
+ qbsWarning() << error.toString();
+ }
+}
+
+
+InternalBuildJob::InternalBuildJob(QObject *parent) : BuildGraphTouchingJob(parent)
+{
+}
+
+void InternalBuildJob::build(const QList<BuildProduct::Ptr> &products,
+ const BuildOptions &buildOptions)
+{
+ setup(products, buildOptions);
+ QTimer::singleShot(0, this, SLOT(start()));
+}
+
+void InternalBuildJob::start()
+{
+ Executor * const executor = new Executor(this);
+ connect(executor, SIGNAL(finished()), SLOT(handleFinished()));
+ connect(executor, SIGNAL(error(Error)), SLOT(handleError(Error)));
+ executor->setEngine(new ScriptEngine(this));
+ executor->setBuildOptions(buildOptions());
+ executor->setProgressObserver(observer());
+ executor->build(products());
+}
+
+void InternalBuildJob::handleFinished()
+{
+ storeBuildGraph();
+ if (!hasError()) // Already emitted finished() in that case.
+ emit finished(this);
+}
+
+void InternalBuildJob::handleError(const Error &error)
+{
+ setError(error);
+ emit finished(this);
+}
+
+
+InternalCleanJob::InternalCleanJob(QObject *parent) : BuildGraphTouchingJob(parent)
+{
+}
+
+void InternalCleanJob::clean(const QList<BuildProduct::Ptr> &products, const BuildOptions &buildOptions,
+ bool cleanAll)
+{
+ setup(products, buildOptions);
+ m_cleanAll = cleanAll;
+ QTimer::singleShot(0, this, SLOT(start()));
+}
+
+void InternalCleanJob::start()
+{
+ QFutureWatcher<void> * const watcher = new QFutureWatcher<void>(this);
+ connect(watcher, SIGNAL(finished()), SLOT(handleFinished()));
+ watcher->setFuture(QtConcurrent::run(this, &InternalCleanJob::doClean));
+}
+
+void InternalCleanJob::handleFinished()
+{
+ emit finished(this);
+}
+
+void InternalCleanJob::doClean()
+{
+ try {
+ ArtifactCleaner cleaner;
+ cleaner.cleanup(products(), m_cleanAll, buildOptions());
+ } catch (const Error &error) {
+ setError(error);
+ }
+ storeBuildGraph();
+}
+
+
+ErrorJob::ErrorJob(QObject *parent) : InternalJob(parent)
+{
+}
+
+void ErrorJob::reportError(const Error &error)
+{
+ setError(error);
+ QTimer::singleShot(0, this, SLOT(handleFinished()));
+}
+
+void ErrorJob::handleFinished()
+{
+ emit finished(this);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/api/internaljobs.h b/src/lib/api/internaljobs.h
new file mode 100644
index 000000000..8eae7682d
--- /dev/null
+++ b/src/lib/api/internaljobs.h
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 QBSJOB_H
+#define QBSJOB_H
+
+#include "projectdata.h"
+#include <tools/buildoptions.h>
+#include <tools/error.h>
+#include <buildgraph/buildgraph.h>
+
+#include <QMutex>
+#include <QObject>
+#include <QWaitCondition>
+
+namespace qbs {
+
+namespace Internal {
+class JobObserver;
+
+class InternalJob : public QObject
+{
+ Q_OBJECT
+ friend class JobObserver;
+public:
+ void cancel();
+ Error error() const { return m_error; }
+ bool hasError() const { return !error().entries().isEmpty(); }
+
+protected:
+ explicit InternalJob(QObject *parent = 0);
+
+ JobObserver *observer() const { return m_observer; }
+ void setError(const Error &error) { m_error = error; }
+
+signals:
+ void finished(Internal::InternalJob *job);
+ void newTaskStarted(const QString &description, int totalEffort, Internal::InternalJob *job);
+ void taskProgress(int value, Internal::InternalJob *job);
+
+private:
+ Error m_error;
+ JobObserver * const m_observer;
+};
+
+
+class InternalSetupProjectJob : public InternalJob
+{
+ Q_OBJECT
+public:
+ InternalSetupProjectJob(QObject *parent = 0);
+ ~InternalSetupProjectJob();
+
+ void resolve(const QString &projectFilePath, const QString &buildRoot,
+ const QVariantMap &buildConfig);
+
+ BuildProject::Ptr buildProject() const { return m_buildProject; }
+
+private slots:
+ void start();
+ void handleFinished();
+
+private:
+ void doResolve();
+ void execute();
+
+ bool m_running;
+ QMutex m_runMutex;
+ QWaitCondition m_runWaitCondition;
+
+ QString m_projectFilePath;
+ QString m_buildRoot;
+ QVariantMap m_buildConfig;
+ BuildProject::Ptr m_buildProject;
+};
+
+
+class BuildGraphTouchingJob : public InternalJob
+{
+ Q_OBJECT
+public:
+ const QList<BuildProduct::Ptr> &products() const { return m_products; }
+
+protected:
+ BuildGraphTouchingJob(QObject *parent = 0);
+
+ void setup(const QList<BuildProduct::Ptr> &products, const BuildOptions &buildOptions);
+ const BuildOptions &buildOptions() const { return m_buildOptions; }
+ void storeBuildGraph();
+
+private:
+ QList<BuildProduct::Ptr> m_products;
+ BuildOptions m_buildOptions;
+};
+
+
+class InternalBuildJob : public BuildGraphTouchingJob
+{
+ Q_OBJECT
+public:
+ InternalBuildJob(QObject *parent = 0);
+
+ void build(const QList<BuildProduct::Ptr> &products, const BuildOptions &buildOptions);
+
+private slots:
+ void start();
+ void handleFinished();
+ void handleError(const Error &error);
+};
+
+
+class InternalCleanJob : public BuildGraphTouchingJob
+{
+ Q_OBJECT
+public:
+ InternalCleanJob(QObject *parent = 0);
+
+ void clean(const QList<BuildProduct::Ptr> &products, const BuildOptions &buildOptions,
+ bool cleanAll);
+
+private slots:
+ void start();
+ void handleFinished();
+
+private:
+ void doClean();
+
+ bool m_cleanAll;
+};
+
+
+class ErrorJob : public InternalJob
+{
+ Q_OBJECT
+public:
+ ErrorJob(QObject *parent);
+
+ void reportError(const Error &error);
+
+private slots:
+ void handleFinished();
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBSJOB_H
diff --git a/src/lib/api/jobs.cpp b/src/lib/api/jobs.cpp
new file mode 100644
index 000000000..908eb4696
--- /dev/null
+++ b/src/lib/api/jobs.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "jobs.h"
+
+#include "internaljobs.h"
+#include "project.h"
+#include <language/scriptengine.h>
+
+namespace qbs {
+using namespace Internal;
+
+/*!
+ * \class AbstractJob
+ * \brief The \c AbstractJob class represents an operation relating to a \c Project.
+ * Concrete child classes of \c AbstractJob are created by factory functions in the \c Project
+ * class. The respective objects represent an operation that is started automatically
+ * and is considered "running" until the \c finished() signal has been emitted. Afterwards,
+ * callers can find out whether the operation was successful by calling \c hasError(). While
+ * the operation is going on, progress information is being provided via \c taskStarted() and
+ * \c taskProgress.
+ * Note that though a job is being started automatically by its factory function, you are guaranteed
+ * to recevieve all signals it emits if you connect to it right after getting the object from the
+ * creating function.
+ * \sa Project
+ */
+
+/*!
+ * \enum AbstractJob::State
+ * This enum type specifies which states a job can be in.
+ * \value StateRunning The respective operation is ongoing.
+ * \value StateCanceling The job has been requested to cancel via \c AbstractJob::cancel(),
+ * but the \c AbstractJob::finished() signal has not been emitted yet.
+ * \value StateFinished The operation has finished and the \c AbstractJob::finished() signal
+ * has been emitted.
+ */
+
+ /*!
+ * \fn AbstractJob::State AbstractJob::state() const
+ * \brief Returns the current state of the operation.
+ */
+
+ /*!
+ * \fn bool AbstractJob::hasError() const
+ * \brief Returns true if the operation has finished with an error, otherwise returns false.
+ * This function should not be called before the \c finished() signal has been emitted.
+ */
+
+/*!
+ * \fn void AbstractJob::taskStarted(const QString &description, int maximumProgressValue, qbs::AbstractJob *job)
+ * \brief Indicates that a new task has been started.
+ * The \a description parameter is a string intended for presentation to a user.
+ * The \a maximumProgressValue parameter indicates the maximum value to which subsequent values of
+ * \c taskProgress() will go.
+ * This signal is typically emitted exactly once for a job that finishes successfully. However,
+ * operations might emit it several times if they are made up of subtasks whose overall effort
+ * cannot be determined in advance.
+ * \sa AbstractJob::taskProgress()
+ */
+
+/*!
+ * \fn void taskProgress(int newProgressValue, qbs::AbstractJob *job)
+ * \brief Indicates progress in executing the operation.
+ * The \a newProgressValue parameter represents the current progress. It is always greater than
+ * zero, strictly increasing and goes up to the \c maximumProgressValue argument of the last
+ * call to \c taskStarted().
+ * \sa AbstractJob::taskStarted()
+ */
+
+ /*!
+ * \fn void finished(bool success, qbs::AbstractJob *job)
+ * \brief Indicates that the operation has finished.
+ * Check the \a success parameter to find out whether everything went fine or an error occurred.
+ */
+
+AbstractJob::AbstractJob(InternalJob *internalJob, QObject *parent)
+ : QObject(parent), m_internalJob(internalJob)
+{
+ m_internalJob->setParent(this);
+ connect(m_internalJob, SIGNAL(newTaskStarted(QString,int,Internal::InternalJob*)),
+ SLOT(handleTaskStarted(QString,int)));
+ connect(m_internalJob, SIGNAL(taskProgress(int,Internal::InternalJob*)),
+ SLOT(handleTaskProgress(int)));
+ connect(m_internalJob, SIGNAL(finished(Internal::InternalJob *)), SLOT(handleFinished()));
+ m_state = StateRunning;
+}
+
+/*!
+ * \brief Destroys the object, canceling the operation if necessary.
+ */
+AbstractJob::~AbstractJob()
+{
+ m_internalJob->disconnect(this);
+ cancel();
+}
+
+/*!
+ * \brief Returns the error which caused this operation to fail, if it did fail.
+ */
+Error AbstractJob::error() const
+{
+ return internalJob()->error();
+}
+
+/*!
+ * \brief Cancels this job.
+ * Note that the job might not finish immediately. If you need to make sure it has actually
+ * finished, wait for the \c finished() signal.
+ * \sa AbstractJob::finished(AbstractJob *);
+ */
+void AbstractJob::cancel()
+{
+ if (m_state != StateRunning)
+ return;
+ m_state = StateCanceling;
+ internalJob()->cancel();
+}
+
+void AbstractJob::handleTaskStarted(const QString &description, int maximumProgressValue)
+{
+ emit taskStarted(description, maximumProgressValue, this);
+}
+
+void AbstractJob::handleTaskProgress(int newProgressValue)
+{
+ emit taskProgress(newProgressValue, this);
+}
+
+void AbstractJob::handleFinished()
+{
+ Q_ASSERT(m_state != StateFinished);
+ m_state = StateFinished;
+ emit finished(!hasError(), this);
+}
+
+
+/*!
+ * \class SetupProjectJob
+ * \brief The \c SetupProjectJob class represents an operation that reads a qbs project file and
+ * creates a \c Project object from it.
+ * Note that this job can emit the \c taskStarted() signal more than once.
+ * \sa AbstractJob::taskStarted()
+ */
+
+SetupProjectJob::SetupProjectJob(QObject *parent) : AbstractJob(new InternalSetupProjectJob, parent)
+{
+}
+
+/*!
+ * \brief Returns the project resulting from this operation.
+ * Note that the result is undefined if the job did not finish successfully.
+ * \sa AbstractJob::hasError()
+ */
+Project SetupProjectJob::project() const
+{
+ const InternalSetupProjectJob * const job
+ = qobject_cast<InternalSetupProjectJob *>(internalJob());
+ return job->buildProject();
+}
+
+void SetupProjectJob::resolve(const QString &projectFilePath, const QString &buildRoot,
+ const QVariantMap &buildConfig)
+{
+ InternalSetupProjectJob * const job = qobject_cast<InternalSetupProjectJob *>(internalJob());
+ job->resolve(projectFilePath, buildRoot, buildConfig);
+}
+
+
+/*!
+ * \class BuildJob
+ * \brief The \c BuildJob class represents a build operation.
+ */
+
+BuildJob::BuildJob(QObject *parent) : AbstractJob(new InternalBuildJob, parent)
+{
+}
+
+void BuildJob::build(const QList<BuildProduct::Ptr> &products, const BuildOptions &options)
+{
+ qobject_cast<InternalBuildJob *>(internalJob())->build(products, options);
+}
+
+
+/*!
+ * \class CleanJob
+ * \brief The \c CleanJob class represents an operation removing build artifacts.
+ */
+
+CleanJob::CleanJob(QObject *parent) : AbstractJob(new InternalCleanJob, parent)
+{
+}
+
+void CleanJob::clean(const QList<BuildProduct::Ptr> &products,
+ const BuildOptions &options, bool cleanAll)
+{
+ qobject_cast<InternalCleanJob *>(internalJob())->clean(products, options, cleanAll);
+}
+
+} // namespace qbs
diff --git a/src/lib/api/jobs.h b/src/lib/api/jobs.h
new file mode 100644
index 000000000..a64f0a547
--- /dev/null
+++ b/src/lib/api/jobs.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 JOBS_H
+#define JOBS_H
+
+#include <tools/error.h>
+
+#include <QObject>
+#include <QSharedPointer>
+#include <QVariantMap>
+
+namespace qbs {
+class BuildOptions;
+namespace Internal {
+class BuildProduct;
+class InternalJob;
+class ProjectPrivate;
+} // namespace Internal
+
+class Project;
+
+class AbstractJob : public QObject
+{
+ Q_OBJECT
+public:
+ ~AbstractJob();
+
+ enum State { StateRunning, StateCanceling, StateFinished };
+ State state() const { return m_state; }
+
+ Error error() const;
+ bool hasError() const { return !error().entries().isEmpty(); }
+
+ void cancel();
+
+protected:
+ AbstractJob(Internal::InternalJob *internalJob, QObject *parent);
+ Internal::InternalJob *internalJob() const { return m_internalJob; }
+
+signals:
+ void taskStarted(const QString &description, int maximumProgressValue, qbs::AbstractJob *job);
+ void taskProgress(int newProgressValue, qbs::AbstractJob *job);
+ void finished(bool success, qbs::AbstractJob *job);
+
+private slots:
+ void handleTaskStarted(const QString &description, int maximumProgressValue);
+ void handleTaskProgress(int newProgressValue);
+ void handleFinished();
+
+private:
+ Internal::InternalJob * const m_internalJob;
+ State m_state;
+};
+
+
+class SetupProjectJob : public AbstractJob
+{
+ Q_OBJECT
+ friend class Project;
+public:
+ Project project() const;
+
+private:
+ SetupProjectJob(QObject *parent);
+
+ void resolve(const QString &projectFilePath, const QString &buildRoot,
+ const QVariantMap &buildConfig);
+};
+
+
+class BuildJob : public AbstractJob
+{
+ Q_OBJECT
+ friend class Internal::ProjectPrivate;
+private:
+ BuildJob(QObject *parent);
+
+ void build(const QList<QSharedPointer<Internal::BuildProduct> > &products,
+ const BuildOptions &options);
+};
+
+
+class CleanJob : public AbstractJob
+{
+ Q_OBJECT
+ friend class Internal::ProjectPrivate;
+private:
+ CleanJob(QObject *parent);
+
+ void clean(const QList<QSharedPointer<Internal::BuildProduct> > &products,
+ const BuildOptions &options, bool cleanAll);
+};
+} // namespace qbs
+
+#endif // JOBS_H
diff --git a/src/lib/api/project.cpp b/src/lib/api/project.cpp
new file mode 100644
index 000000000..600e41873
--- /dev/null
+++ b/src/lib/api/project.cpp
@@ -0,0 +1,423 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "project.h"
+
+#include "jobs.h"
+#include "projectdata.h"
+#include "runenvironment.h"
+#include <buildgraph/artifact.h>
+#include <buildgraph/buildgraph.h>
+#include <language/language.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/platform.h>
+#include <tools/scannerpluginmanager.h>
+#include <tools/scripttools.h>
+#include <tools/settings.h>
+
+#include <QMutex>
+#include <QMutexLocker>
+#include <QSharedData>
+
+namespace qbs {
+namespace Internal {
+
+static bool pluginsLoaded = false;
+static QMutex pluginsLoadedMutex;
+
+static void loadPlugins()
+{
+ QMutexLocker locker(&pluginsLoadedMutex);
+ if (pluginsLoaded)
+ return;
+
+ QStringList pluginPaths;
+ const QStringList settingsPluginPaths = Settings().pluginPaths();
+ foreach (const QString &pluginPath, settingsPluginPaths) {
+ if (!FileInfo::exists(pluginPath)) {
+ qbsWarning() << Tr::tr("Plugin path '%1' does not exist.")
+ .arg(QDir::toNativeSeparators(pluginPath));
+ } else {
+ pluginPaths << pluginPath;
+ }
+ }
+ ScannerPluginManager::instance()->loadPlugins(pluginPaths);
+
+ pluginsLoaded = true;
+}
+
+
+static QVariantMap expandedBuildConfiguration(const QVariantMap &userBuildConfig)
+{
+ QHash<QString, Platform::Ptr > platforms = Platform::platforms();
+ if (platforms.isEmpty())
+ throw Error(Tr::tr("No platforms configured. You must run 'qbs platforms probe' first."));
+
+ Settings settings;
+ QVariantMap expandedConfig = userBuildConfig;
+
+ // Fill in buildCfg in this order (making sure not to overwrite a key already set by a previous stage)
+ // 1) Things specified on command line (already in buildCfg at this point)
+ // 2) Everything from the profile key (in reverse order)
+ // 3) Everything from the platform
+ // 4) Any remaining keys from modules keyspace
+ QString profileName = expandedConfig.value("qbs.profile").toString();
+ if (profileName.isNull()) {
+ profileName = settings.value("profile").toString();
+ if (profileName.isNull())
+ throw Error(Tr::tr("No profile given.\n"
+ "Either set the configuration value 'profile' to a valid profile's name\n"
+ "or specify the profile with the command line parameter 'profile:name'."));
+ expandedConfig.insert("qbs.profile", profileName);
+ }
+
+ // (2)
+ const QString profileGroup = QString("profiles/%1").arg(profileName);
+ const QStringList profileKeys = settings.allKeysWithPrefix(profileGroup);
+ if (profileKeys.isEmpty())
+ throw Error(Tr::tr("Unknown profile '%1'.").arg(profileName));
+ foreach (const QString &profileKey, profileKeys) {
+ QString fixedKey(profileKey);
+ fixedKey.replace(QChar('/'), QChar('.'));
+ if (!expandedConfig.contains(fixedKey))
+ expandedConfig.insert(fixedKey, settings.value(profileGroup + "/" + profileKey));
+ }
+
+ // (3) Need to make sure we have a value for qbs.platform before going any further
+ QVariant platformName = expandedConfig.value("qbs.platform");
+ if (!platformName.isValid()) {
+ platformName = settings.moduleValue("qbs/platform", profileName);
+ if (!platformName.isValid())
+ throw Error(Tr::tr("No platform given and no default set."));
+ expandedConfig.insert("qbs.platform", platformName);
+ }
+ Platform::Ptr platform = platforms.value(platformName.toString());
+ if (platform.isNull())
+ throw Error(Tr::tr("Unknown platform '%1'.").arg(platformName.toString()));
+ foreach (const QString &key, platform->settings.allKeys()) {
+ if (key.startsWith(Platform::internalKey()))
+ continue;
+ QString fixedKey = key;
+ int idx = fixedKey.lastIndexOf(QChar('/'));
+ if (idx > 0)
+ fixedKey[idx] = QChar('.');
+ if (!expandedConfig.contains(fixedKey))
+ expandedConfig.insert(fixedKey, platform->settings.value(key));
+ }
+ // Now finally do (4)
+ foreach (const QString &defaultKey, settings.allKeysWithPrefix("modules")) {
+ QString fixedKey(defaultKey);
+ fixedKey.replace(QChar('/'), QChar('.'));
+ if (!expandedConfig.contains(fixedKey))
+ expandedConfig.insert(fixedKey, settings.value(QString("modules/") + defaultKey));
+ }
+
+ if (!expandedConfig.value("qbs.buildVariant").isValid())
+ throw Error(Tr::tr("Property 'qbs.buildVariant' missing in build configuration."));
+
+ foreach (const QString &property, expandedConfig.keys()) {
+ QStringList nameElements = property.split('.');
+ if (nameElements.count() == 1 && nameElements.first() != "project") // ### Still need this because platform doesn't supply fully-qualified properties (yet), need to fix this
+ nameElements.prepend("qbs");
+ if (nameElements.count() > 2) { // ### workaround for submodules being represented internally as a single module of name "module/submodule" rather than two nested modules "module" and "submodule"
+ QStringList newElements(QStringList(nameElements.mid(0, nameElements.count()-1)).join("/"));
+ newElements.append(nameElements.last());
+ nameElements = newElements;
+ }
+ setConfigProperty(expandedConfig, nameElements, expandedConfig.value(property));
+ expandedConfig.remove(property);
+ }
+
+ return expandedConfig;
+}
+
+
+class ProjectPrivate : public QSharedData
+{
+public:
+ ProjectPrivate(const BuildProject::Ptr &internalProject)
+ : internalProject(internalProject), m_projectDataRetrieved(false)
+ {
+ }
+
+ ProjectData projectData();
+ BuildJob *buildProducts(const QList<BuildProduct::Ptr> &products, const BuildOptions &options,
+ bool needsDepencencyResolving, QObject *jobOwner);
+ CleanJob *cleanProducts(const QList<BuildProduct::Ptr> &products, const BuildOptions &options,
+ Project::CleanType cleanType, QObject *jobOwner);
+ QList<BuildProduct::Ptr> internalProducts(const QList<ProductData> &products) const;
+ BuildProduct::Ptr internalProduct(const ProductData &product) const;
+
+ const BuildProject::Ptr internalProject;
+
+private:
+ void retrieveProjectData();
+
+ ProjectData m_projectData;
+ bool m_projectDataRetrieved;
+};
+
+ProjectData ProjectPrivate::projectData()
+{
+ if (!m_projectDataRetrieved)
+ retrieveProjectData();
+ return m_projectData;
+}
+
+BuildJob *ProjectPrivate::buildProducts(const QList<BuildProduct::Ptr> &products,
+ const BuildOptions &options, bool needsDepencencyResolving, QObject *jobOwner)
+{
+ QList<BuildProduct::Ptr> productsToBuild = products;
+ if (needsDepencencyResolving) {
+ for (int i = 0; i < productsToBuild.count(); ++i) {
+ const BuildProduct::ConstPtr &product = productsToBuild.at(i);
+ foreach (const BuildProduct::Ptr &dependency, product->dependencies) {
+ if (!productsToBuild.contains(dependency))
+ productsToBuild << dependency;
+ }
+ }
+ }
+
+ BuildJob * const job = new BuildJob(jobOwner);
+ job->build(productsToBuild, options);
+ return job;
+}
+
+CleanJob *ProjectPrivate::cleanProducts(const QList<BuildProduct::Ptr> &products,
+ const BuildOptions &options, Project::CleanType cleanType, QObject *jobOwner)
+{
+ CleanJob * const job = new CleanJob(jobOwner);
+ job->clean(products, options, cleanType == Project::CleanupAll);
+ return job;
+}
+
+QList<BuildProduct::Ptr> ProjectPrivate::internalProducts(const QList<ProductData> &products) const
+{
+ QList<Internal::BuildProduct::Ptr> internalProducts;
+ foreach (const ProductData &product, products)
+ internalProducts << internalProduct(product);
+ return internalProducts;
+}
+
+BuildProduct::Ptr ProjectPrivate::internalProduct(const ProductData &product) const
+{
+ foreach (const Internal::BuildProduct::Ptr &buildProduct, internalProject->buildProducts()) {
+ if (product.name() == buildProduct->rProduct->name)
+ return buildProduct;
+ }
+ qFatal("No build product '%s'", qPrintable(product.name()));
+ return BuildProduct::Ptr();
+}
+
+void ProjectPrivate::retrieveProjectData()
+{
+ m_projectData.m_qbsFilePath = internalProject->resolvedProject()->qbsFile;
+ foreach (const BuildProduct::Ptr &buildProduct, internalProject->buildProducts()) {
+ const ResolvedProduct::ConstPtr resolvedProduct = buildProduct->rProduct;
+ ProductData product;
+ product.m_name = resolvedProduct->name;
+ product.m_qbsFilePath = resolvedProduct->qbsFile;
+ product.m_qbsLine = resolvedProduct->qbsLine;
+ product.m_fileTags = resolvedProduct->fileTags;
+ foreach (const ResolvedGroup::Ptr &resolvedGroup, resolvedProduct->groups) {
+ GroupData group;
+ group.m_name = resolvedGroup->name;
+ group.m_qbsLine = resolvedGroup->qbsLine;
+ foreach (const SourceArtifact::ConstPtr &sa, resolvedGroup->files)
+ group.m_filePaths << sa->absoluteFilePath;
+ if (resolvedGroup->wildcards) {
+ foreach (const SourceArtifact::ConstPtr &sa, resolvedGroup->wildcards->files)
+ group.m_expandedWildcards << sa->absoluteFilePath;
+ }
+ group.m_properties = resolvedGroup->properties->value();
+ product.m_groups << group;
+ }
+ m_projectData.m_products << product;
+ }
+ m_projectDataRetrieved = true;
+}
+
+} // namespace Internal
+
+
+/*!
+ * \enum Project::CleanType
+ * This enum type specifies which kind of build artifacts to remove.
+ * \value CleanupAll Indicates that all files created by the build process should be removed.
+ * \value CleanupTemporaries Indicates that only intermediate build artifacts should be removed.
+ * If, for example, the product to clean up for is a Linux shared library, the .so file
+ * would be left on the disk, but the .o files would be removed.
+ */
+
+ /*!
+ * \class Project
+ * \brief The \c Project class provides services related to a qbs project.
+ */
+
+Project::Project(const Internal::BuildProject::Ptr &internalProject)
+ : d(new Internal::ProjectPrivate(internalProject))
+{
+}
+
+Project::Project(const Project &other) : d(other.d)
+{
+}
+
+Project::~Project()
+{
+}
+
+Project &Project::operator=(const Project &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ * \brief Sets up a \c Project from a source file, possibly re-using previously stored information.
+ * The \a projectFilePath parameter is the path to the project file, typically ending in ".qbs".
+ * The \a buildConfig parameter is the set of properties used for resolving the project. Note that
+ * calling this function with the same \a projectFilePath and different \a buildConfig parameters
+ * will result in two distinct \c Projects, since the different properties will potentially cause
+ * the bindings in the project file to evaluate to different values.
+ * The \a buildRoot parameter is the base directory for building the project. It will be used
+ * to derive the actual build directory and is required here because the project information might
+ * already exist on disk, in which case it will be available faster. If you know that the project
+ * has never been built and you do not plan to do so later, \a buildRoot can be an arbitrary string.
+ * The function will finish immediately, returning a \c SetupProjectJob which can be used to
+ * track the results of the operation.
+ */
+SetupProjectJob *Project::setupProject(const QString &projectFilePath,
+ const QVariantMap &buildConfig, const QString &buildRoot, QObject *jobOwner)
+{
+ loadPlugins();
+ SetupProjectJob * const job = new SetupProjectJob(jobOwner);
+ job->resolve(projectFilePath, buildRoot, expandedBuildConfiguration(buildConfig));
+ return job;
+}
+
+/*!
+ * \brief Retrieves information for this project.
+ * Call this function if you need insight into the project structure, e.g. because you want to know
+ * which products or files are in it.
+ */
+ProjectData Project::projectData() const
+{
+ return d->projectData();
+}
+
+/*!
+ * \brief Returns the file path of the executable associated with the given product.
+ * If the product is not an application, an empty string is returned.
+ */
+QString Project::targetExecutable(const ProductData &product) const
+{
+ const Internal::BuildProduct::ConstPtr buildProduct = d->internalProduct(product);
+ if (!buildProduct->rProduct->fileTags.contains(QLatin1String("application")))
+ return QString();
+
+ foreach (const Internal::Artifact * const artifact, buildProduct->targetArtifacts) {
+ if (artifact->fileTags.contains(QLatin1String("application")))
+ return artifact->filePath();
+ }
+ return QString();
+}
+
+RunEnvironment Project::getRunEnvironment(const ProductData &product,
+ const QProcessEnvironment &environment) const
+{
+ const Internal::ResolvedProduct::Ptr resolvedProduct = d->internalProduct(product)->rProduct;
+ return RunEnvironment(resolvedProduct, environment);
+}
+
+/*!
+ * \brief Causes all products of this project to be built, if necessary.
+ * The function will finish immediately, returning a \c BuildJob identifiying the operation.
+ */
+BuildJob *Project::buildAllProducts(const BuildOptions &options, QObject *jobOwner) const
+{
+ return d->buildProducts(d->internalProject->buildProducts().toList(), options, false, jobOwner);
+}
+
+/*!
+ * \brief Causes the specified list of products to be built.
+ * Use this function if you only want to build some products, not the whole project. If any of
+ * the products in \a products depend on other products, those will also be built.
+ * The function will finish immediately, returning a \c BuildJob identifiying the operation.
+ */
+BuildJob *Project::buildSomeProducts(const QList<ProductData> &products,
+ const BuildOptions &options, QObject *jobOwner) const
+{
+ return d->buildProducts(d->internalProducts(products), options, true, jobOwner);
+}
+
+/*!
+ * \brief Convenience function for \c buildSomeProducts().
+ * \sa Project::buildSomeProducts().
+ */
+BuildJob *Project::buildOneProduct(const ProductData &product, const BuildOptions &options,
+ QObject *jobOwner) const
+{
+ return buildSomeProducts(QList<ProductData>() << product, options, jobOwner);
+}
+
+/*!
+ * \brief Removes the build artifacts of all products in the project.
+ * The function will finish immediately, returning a \c BuildJob identifiying this operation.
+ */
+CleanJob *Project::cleanAllProducts(const BuildOptions &options, CleanType cleanType,
+ QObject *jobOwner) const
+{
+ return d->cleanProducts(d->internalProject->buildProducts().toList(), options, cleanType,
+ jobOwner);
+}
+
+/*!
+ * \brief Removes the build artifacts of the given products.
+ * The function will finish immediately, returning a \c BuildJob identifiying this operation.
+ */
+CleanJob *Project::cleanSomeProducts(const QList<ProductData> &products,
+ const BuildOptions &options, CleanType cleanType, QObject *jobOwner) const
+{
+ return d->cleanProducts(d->internalProducts(products), options, cleanType, jobOwner);
+}
+
+/*!
+ * \brief Convenience function for \c cleanSomeProducts().
+ * \sa Project::cleanSomeProducts().
+ */
+CleanJob *Project::cleanOneProduct(const ProductData &product, const BuildOptions &options,
+ CleanType cleanType, QObject *jobOwner) const
+{
+ return cleanSomeProducts(QList<ProductData>() << product, options, cleanType, jobOwner);
+}
+
+} // namespace qbs
diff --git a/src/lib/api/project.h b/src/lib/api/project.h
new file mode 100644
index 000000000..63e957673
--- /dev/null
+++ b/src/lib/api/project.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** 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 QBS_PROJECT_H
+#define QBS_PROJECT_H
+
+#include <QExplicitlySharedDataPointer>
+#include <QList>
+#include <QSharedPointer>
+#include <QVariantMap>
+
+QT_BEGIN_NAMESPACE
+class QObject;
+class QProcessEnvironment;
+QT_END_NAMESPACE
+
+namespace qbs {
+class BuildJob;
+class BuildOptions;
+class CleanJob;
+class ProductData;
+class ProjectData;
+class RunEnvironment;
+class SetupProjectJob;
+
+namespace Internal {
+class BuildProject;
+class ProjectPrivate;
+} // namespace Internal;
+
+class Project
+{
+ friend class SetupProjectJob;
+ friend uint qHash(const Project &p);
+public:
+ static SetupProjectJob *setupProject(const QString &projectFilePath,
+ const QVariantMap &buildConfig, const QString &buildRoot, QObject *jobOwner);
+
+ Project(const Project &other);
+ Project &operator=(const Project &other);
+ ~Project();
+
+ ProjectData projectData() const;
+ QString targetExecutable(const ProductData &product) const;
+ RunEnvironment getRunEnvironment(const ProductData &product,
+ const QProcessEnvironment &environment) const;
+
+ BuildJob *buildAllProducts(const BuildOptions &options, QObject *jobOwner = 0) const;
+ BuildJob *buildSomeProducts(const QList<ProductData> &products, const BuildOptions &options,
+ QObject *jobOwner = 0) const;
+ BuildJob *buildOneProduct(const ProductData &product, const BuildOptions &options,
+ QObject *jobOwner = 0) const;
+
+ enum CleanType { CleanupAll, CleanupTemporaries };
+ CleanJob *cleanAllProducts(const BuildOptions &options, CleanType cleanType,
+ QObject *jobOwner = 0) const;
+ CleanJob *cleanSomeProducts(const QList<ProductData> &products, const BuildOptions &options,
+ CleanType cleanType, QObject *jobOwner = 0) const;
+ CleanJob *cleanOneProduct(const ProductData &product, const BuildOptions &options,
+ CleanType cleanType, QObject *jobOwner = 0) const;
+
+ bool operator==(const Project &other) const { return d.data() == other.d.data(); }
+
+private:
+ Project();
+ Project(const QSharedPointer<Internal::BuildProject> &internalProject);
+
+ QExplicitlySharedDataPointer<Internal::ProjectPrivate> d;
+};
+
+inline bool operator!=(const Project &p1, const Project &p2) { return !(p1 == p2); }
+inline uint qHash(const Project &p) { return QT_PREPEND_NAMESPACE(qHash)(p.d.data()); }
+
+} // namespace qbs
+
+#endif // QBS_PROJECT_H
diff --git a/src/lib/language/publictypes.cpp b/src/lib/api/projectdata.cpp
index dde0279cc..c11154f31 100644
--- a/src/lib/language/publictypes.cpp
+++ b/src/lib/api/projectdata.cpp
@@ -26,113 +26,108 @@
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
-#include "publictypes.h"
+#include "projectdata.h"
namespace qbs {
// These are not inline because MSVC does not like it when source files have no content.
-Group::Group() { }
-Product::Product() { }
-Project::Project() { }
+GroupData::GroupData() { }
+ProductData::ProductData() { }
+ProjectData::ProjectData() { }
/*!
- * \class Group
- * \brief The \c Group class corresponds to the respective item in a qbs source file.
+ * \class GroupData
+ * \brief The \c GroupData class corresponds to the Group item in a qbs source file.
*/
/*!
- * \fn ObjectId Group::id() const
- * \brief Uniquely identifies a group.
- */
-
- /*!
- * \fn QString Group::name() const
+ * \fn QString GroupData::name() const
* \brief The name of the group.
*/
+/*!
+ * \fn int GroupData::qbsLine() const
+ * \brief The line at which the group is defined in the respective source file.
+ */
+
/*!
- * \fn QStringList Group::filePaths() const
+ * \fn QStringList GroupData::filePaths() const
* \brief The files listed in the group item's "files" binding.
* Note that these do not include expanded wildcards.
- * \sa Group::expandedWildcards
+ * \sa GroupData::expandedWildcards
*/
/*!
- * \fn QStringList Group::expandedWildcards() const
+ * \fn QStringList GroupData::expandedWildcards() const
* \brief The list of files resulting from expanding all wildcard patterns in the group.
*/
/*!
- * \fn QVariantMap Group::properties() const
+ * \fn QVariantMap GroupData::properties() const
* \brief The set of properties valid in this group.
- * Typically, most of them are inherited from the respective \c Product.
+ * Typically, most of them are inherited from the respective Product.
*/
/*!
- * \fn QStringList Group::allFilePaths() const
+ * \fn QStringList GroupData::allFilePaths() const
* \brief All files in this group, regardless of how whether they were given explicitly
* or via wildcards.
- * \sa Group::filePaths
- * \sa Group::expandedWildcards
+ * \sa GroupData::filePaths
+ * \sa GroupData::expandedWildcards
*/
/*!
- * \class Product
- * \brief The \c Product class corresponds to the respective item in a qbs source file.
+ * \class ProductData
+ * \brief The \c ProductData class corresponds to the Product item in a qbs source file.
*/
- /*!
- * \fn ObjectId Product::id() const
- * \brief Uniquely identifies a product.
- */
-
/*!
- * \fn QString Product::name() const
+ * \fn QString ProductData::name() const
* \brief The name of the product as given in the qbs source file.
*/
/*!
- * \fn QString Product::qbsFilePath() const
+ * \fn QString ProductData::qbsFilePath() const
* \brief The qbs source file in which the product is defined.
*/
/*!
- * \fn QString Product::fileTags() const
+ * \fn int ProductData::qbsLine() const
+ * \brief The line in at which the product is defined in the source file.
+ */
+
+/*!
+ * \fn QString ProductData::fileTags() const
* \brief The file tags of this product. Corresponds to a Product's "type" property in
* a qbs source file.
*/
/*!
- * \fn QVariantMap product::properties() const
+ * \fn QVariantMap ProductData::properties() const
* \brief The set of properties valid in this product.
- * Note that a \c Group can override product properties.
- * \sa Group::properties()
+ * Note that product properties can be overwritten in a Group.
+ * \sa GroupData::properties()
*/
/*!
- * \fn QList<Group> groups() const
- * \brief The list of \c Groups in this product.
+ * \fn QList<GroupData> groups() const
+ * \brief The list of \c GroupData in this product.
*/
/*!
- * \class Project
- * \brief The \c Project class corresponds to the respective item in a qbs source file.
- */
-
-/*!
- * \fn ObjectId Project::id() const
- * \brief Uniquely identifies a project.
+ * \class ProjectData
+ * \brief The \c ProjectData class corresponds to the Project item in a qbs source file.
*/
/*!
- * \fn QString Project::qbsFilePath() const
+ * \fn QString ProjectData::qbsFilePath() const
* \brief The qbs source file in which the project is defined.
*/
/*!
- * \fn QList<Product> Project::products() const
+ * \fn QList<ProductData> ProjectData::products() const
* \brief The products in this project.
*/
} // namespace qbs
diff --git a/src/lib/language/publictypes.h b/src/lib/api/projectdata.h
index 7768d88ea..2775761f5 100644
--- a/src/lib/language/publictypes.h
+++ b/src/lib/api/projectdata.h
@@ -29,8 +29,6 @@
#ifndef PUBLICTYPES_H
#define PUBLICTYPES_H
-#include <QtGlobal>
-#include <QDateTime>
#include <QList>
#include <QPair>
#include <QString>
@@ -38,44 +36,16 @@
#include <QVariantMap>
namespace qbs {
+namespace Internal { class ProjectPrivate; }
-template<int dummy> class IdTemplate;
-template<int n> uint qHash(IdTemplate<n> id);
+// TODO: explicitly shared?
-// Instantiate this template with a unique parameter to get a new type-safe id class.
-template<int dummy> class IdTemplate
+class GroupData
{
- friend class QbsEngine;
- friend uint qHash<>(IdTemplate<dummy> id);
+ friend class Internal::ProjectPrivate;
public:
- IdTemplate() : m_id(0), m_timeStamp(0) {}
+ GroupData();
- bool isValid() const { return m_id; }
- bool operator==(IdTemplate<dummy> other) const {
- return m_id == other.m_id && m_timeStamp == other.m_timeStamp;
- }
-
-private:
- explicit IdTemplate(quintptr id) : m_id(id), m_timeStamp(QDateTime::currentMSecsSinceEpoch()) {}
-
- quintptr m_id;
- qint64 m_timeStamp;
-};
-
-template<int n> uint qHash(IdTemplate<n> id) {
- return QT_PREPEND_NAMESPACE(qHash)(qMakePair(id.m_id, id.m_timeStamp));
-}
-
-
-class Group
-{
- friend class QbsEngine;
-public:
- typedef IdTemplate<0> Id;
-
- Group();
-
- Id id() const { return m_id; }
int qbsLine() const { return m_qbsLine; }
QString name() const { return m_name; }
QStringList filePaths() const { return m_filePaths; }
@@ -86,9 +56,6 @@ public:
QStringList allFilePaths() const { return filePaths() + expandedWildcards(); }
private:
- explicit Group(Id id) : m_id(id) {}
-
- Id m_id;
QString m_name;
int m_qbsLine;
QStringList m_filePaths;
@@ -97,53 +64,41 @@ private:
};
-class Product
+class ProductData
{
- friend class QbsEngine;
+ friend class Internal::ProjectPrivate;
public:
- typedef IdTemplate<1> Id;
+ ProductData();
- Product();
-
- Id id() const { return m_id; }
QString name() const { return m_name; }
QString qbsFilePath() const { return m_qbsFilePath; }
int qbsLine() const { return m_qbsLine; }
QStringList fileTags() const { return m_fileTags; }
QVariantMap properties() const { return m_properties; }
- QList<Group> groups() const { return m_groups; }
+ QList<GroupData> groups() const { return m_groups; }
private:
- explicit Product(Id id) : m_id(id) {}
-
- Id m_id;
QString m_name;
QString m_qbsFilePath;
int m_qbsLine;
QStringList m_fileTags;
QVariantMap m_properties;
- QList<Group> m_groups;
+ QList<GroupData> m_groups;
};
-class Project
+class ProjectData
{
- friend class QbsEngine;
+ friend class Internal::ProjectPrivate;
public:
- typedef IdTemplate<2> Id;
+ ProjectData();
- Project();
-
- Id id() const { return m_id; }
QString qbsFilePath() const { return m_qbsFilePath; }
- QList<Product> products() const { return m_products; }
+ QList<ProductData> products() const { return m_products; }
private:
- explicit Project(Id id) : m_id(id) {}
-
- Id m_id;
QString m_qbsFilePath;
- QList<Product> m_products;
+ QList<ProductData> m_products;
};
} // namespace qbs
diff --git a/src/lib/tools/runenvironment.cpp b/src/lib/api/runenvironment.cpp
index 3076825c3..238a8b628 100644
--- a/src/lib/tools/runenvironment.cpp
+++ b/src/lib/api/runenvironment.cpp
@@ -29,9 +29,9 @@
#include "runenvironment.h"
+#include <api/projectdata.h>
+#include <buildgraph/buildgraph.h>
#include <language/language.h>
-#include <language/publicobjectsmap.h>
-#include <language/publictypes.h>
#include <language/scriptengine.h>
#include <logging/logger.h>
#include <logging/translator.h>
@@ -53,20 +53,16 @@ using namespace Internal;
class RunEnvironment::RunEnvironmentPrivate
{
public:
- ScriptEngine *engine;
+ ScriptEngine engine;
ResolvedProduct::Ptr resolvedProduct;
QProcessEnvironment environment;
};
-RunEnvironment::RunEnvironment(ScriptEngine *engine, const Product &product,
- const PublicObjectsMap &publicObjectsMap,
+RunEnvironment::RunEnvironment(const ResolvedProduct::Ptr &product,
const QProcessEnvironment &environment)
: d(new RunEnvironmentPrivate)
{
- d->engine = engine;
- d->resolvedProduct = publicObjectsMap.product(product.id());
- if (!d->resolvedProduct)
- throw Error(Tr::tr("Cannot run target: Invalid product."));
+ d->resolvedProduct = product;
d->environment = environment;
}
@@ -77,7 +73,7 @@ RunEnvironment::~RunEnvironment()
int RunEnvironment::runShell()
{
- d->resolvedProduct->setupBuildEnvironment(d->engine, d->environment);
+ d->resolvedProduct->setupBuildEnvironment(&d->engine, d->environment);
const QString productId = d->resolvedProduct->name;
qbsInfo() << Tr::tr("Starting shell for target '%1'.").arg(productId);
@@ -126,7 +122,7 @@ int RunEnvironment::runTarget(const QString &targetBin, const QStringList &argum
return EXIT_FAILURE;
}
- d->resolvedProduct->setupRunEnvironment(d->engine, d->environment);
+ d->resolvedProduct->setupRunEnvironment(&d->engine, d->environment);
qbsInfo("Starting target '%s'.", qPrintable(QDir::toNativeSeparators(targetBin)));
QProcess process;
diff --git a/src/lib/tools/runenvironment.h b/src/lib/api/runenvironment.h
index 672faffa0..3e8bb1266 100644
--- a/src/lib/tools/runenvironment.h
+++ b/src/lib/api/runenvironment.h
@@ -30,6 +30,7 @@
#ifndef QBS_RUNENVIRONMENT_H
#define QBS_RUNENVIRONMENT_H
+#include <QSharedPointer>
#include <QStringList>
QT_BEGIN_NAMESPACE
@@ -37,16 +38,14 @@ class QProcessEnvironment;
QT_END_NAMESPACE
namespace qbs {
-class Product;
namespace Internal {
-class PublicObjectsMap;
-class ScriptEngine;
+class ResolvedProduct;
} // namespace Internal
class RunEnvironment
{
- friend class QbsEngine;
+ friend class Project;
public:
~RunEnvironment();
@@ -55,8 +54,7 @@ public:
int runTarget(const QString &targetBin, const QStringList &arguments);
private:
- RunEnvironment(Internal::ScriptEngine *engine, const Product &product,
- const Internal::PublicObjectsMap &publicObjectsMap,
+ RunEnvironment(const QSharedPointer<Internal::ResolvedProduct> &product,
const QProcessEnvironment &environment);
class RunEnvironmentPrivate;
diff --git a/src/lib/buildgraph/buildgraph.cpp b/src/lib/buildgraph/buildgraph.cpp
index b0a444430..7b5651b08 100644
--- a/src/lib/buildgraph/buildgraph.cpp
+++ b/src/lib/buildgraph/buildgraph.cpp
@@ -131,20 +131,26 @@ Artifact *BuildProduct::lookupArtifact(const QString &filePath) const
return lookupArtifact(dirPath, fileName);
}
-BuildGraph::BuildGraph(ScriptEngine *engine)
+BuildGraph::BuildGraph()
: m_progressObserver(0)
- , m_engine(engine)
+ , m_engine(0)
, m_initEngineCalls(0)
{
- m_prepareScriptScope = m_engine->newObject();
- ProcessCommand::setupForJavaScript(m_prepareScriptScope);
- JavaScriptCommand::setupForJavaScript(m_prepareScriptScope);
}
BuildGraph::~BuildGraph()
{
}
+void BuildGraph::setEngine(ScriptEngine *engine)
+{
+ m_scriptProgramCache.clear();
+ m_engine = engine;
+ m_prepareScriptScope = m_engine->newObject();
+ ProcessCommand::setupForJavaScript(m_prepareScriptScope);
+ JavaScriptCommand::setupForJavaScript(m_prepareScriptScope);
+}
+
void BuildGraph::insert(BuildProduct::Ptr product, Artifact *n) const
{
insert(product.data(), n);
@@ -282,9 +288,10 @@ static AbstractCommand *createCommandFromScriptValue(const QScriptValue &scriptV
void BuildGraph::createTransformerCommands(const PrepareScript::ConstPtr &script, Transformer *transformer)
{
- QScriptProgram &scriptProgram = m_scriptProgramCache[script->script];
- if (scriptProgram.isNull())
- scriptProgram = QScriptProgram(script->script);
+// QScriptProgram &scriptProgram = m_scriptProgramCache[script->script];
+// if (scriptProgram.isNull())
+// scriptProgram = QScriptProgram(script->script);
+ QScriptProgram scriptProgram = QScriptProgram(script->script);
QScriptValue scriptValue = m_engine->evaluate(scriptProgram);
if (m_engine->hasUncaughtException())
diff --git a/src/lib/buildgraph/buildgraph.h b/src/lib/buildgraph/buildgraph.h
index 652a06a9b..5959fbc20 100644
--- a/src/lib/buildgraph/buildgraph.h
+++ b/src/lib/buildgraph/buildgraph.h
@@ -46,12 +46,12 @@
#include <QVector>
namespace qbs {
-class ProgressObserver;
namespace Internal {
class Artifact;
-class Transformer;
class BuildProject;
+class ProgressObserver;
+class Transformer;
typedef QMap<QString, ArtifactList> ArtifactsPerFileTagMap;
@@ -153,9 +153,10 @@ private:
class BuildGraph
{
public:
- BuildGraph(ScriptEngine *engine);
+ BuildGraph();
~BuildGraph();
+ void setEngine(ScriptEngine *engine);
ScriptEngine *engine() { return m_engine; }
BuildProject::Ptr resolveProject(ResolvedProject::Ptr);
diff --git a/src/lib/buildgraph/executor.cpp b/src/lib/buildgraph/executor.cpp
index b3f930475..3eb75f4fc 100644
--- a/src/lib/buildgraph/executor.cpp
+++ b/src/lib/buildgraph/executor.cpp
@@ -83,8 +83,9 @@ private:
};
-Executor::Executor()
- : m_engine(0)
+Executor::Executor(QObject *parent)
+ : QObject(parent)
+ , m_engine(0)
, m_progressObserver(0)
, m_state(ExecutorIdle)
, m_buildResult(SuccessfulBuild)
@@ -123,8 +124,7 @@ void Executor::build(const QList<BuildProduct::Ptr> &productsToBuild)
try {
doBuild(productsToBuild);
} catch (const Error &e) {
- setError(e.toString());
- return;
+ setError(e);
}
}
@@ -137,6 +137,14 @@ void Executor::doBuild(const QList<BuildProduct::Ptr> &productsToBuild)
m_buildResult = SuccessfulBuild;
m_productsToBuild = productsToBuild;
+ QSet<BuildProject *> projects;
+ foreach (const BuildProduct::ConstPtr &buildProduct, productsToBuild)
+ projects << buildProduct->project;
+ foreach (BuildProject * const project, projects) {
+ project->buildGraph()->setEngine(m_engine);
+ project->buildGraph()->setProgressObserver(m_progressObserver);
+ }
+
initializeArtifactsState();
setState(ExecutorRunning);
Artifact::BuildState initialBuildState = m_buildOptions.changedFiles.isEmpty()
@@ -145,9 +153,6 @@ void Executor::doBuild(const QList<BuildProduct::Ptr> &productsToBuild)
QList<Artifact *> changedArtifacts;
foreach (const QString &filePath, m_buildOptions.changedFiles) {
QList<Artifact *> artifacts;
- QSet<const BuildProject *> projects;
- foreach (const BuildProduct::ConstPtr &buildProduct, productsToBuild)
- projects << buildProduct->project;
foreach (const BuildProject * const project, projects)
artifacts.append(project->lookupArtifacts(filePath));
if (artifacts.isEmpty()) {
@@ -193,8 +198,7 @@ void Executor::cancelBuild()
setState(ExecutorCanceled);
cancelJobs();
m_buildResult = FailedBuild;
- qbsError() << Tr::tr("Build canceled.");
- emit error();
+ emit error(Error(Tr::tr("Build canceled.")));
}
void Executor::setEngine(ScriptEngine *engine)
@@ -625,7 +629,7 @@ void Executor::onProcessError(QString errorString)
ExecutorJob * const job = qobject_cast<ExecutorJob *>(sender());
finishJob(job);
} else {
- setError(errorString);
+ setError(Error(errorString));
finish();
}
}
@@ -756,12 +760,11 @@ void Executor::setState(ExecutorState s)
m_state = s;
}
-void Executor::setError(const QString &errorMessage)
+void Executor::setError(const Error &e)
{
setState(ExecutorError);
- qbsError() << errorMessage;
cancelJobs();
- emit error();
+ emit error(e);
}
} // namespace Internal
diff --git a/src/lib/buildgraph/executor.h b/src/lib/buildgraph/executor.h
index 08d65efc9..e6365e044 100644
--- a/src/lib/buildgraph/executor.h
+++ b/src/lib/buildgraph/executor.h
@@ -41,20 +41,18 @@
#include <QVariant>
namespace qbs {
-class ProgressObserver;
-
namespace Internal {
-
class AutoMoc;
class ExecutorJob;
class InputArtifactScannerContext;
+class ProgressObserver;
class ScanResultCache;
class Executor : public QObject
{
Q_OBJECT
public:
- Executor();
+ Executor(QObject *parent = 0);
~Executor();
void build(const QList<BuildProduct::Ptr> &productsToBuild);
@@ -78,7 +76,7 @@ public:
BuildResult buildResult() const { return m_buildResult; }
signals:
- void error();
+ void error(const Error &error);
void finished();
private slots:
@@ -101,7 +99,7 @@ private:
void finish();
void initializeArtifactsState();
void setState(ExecutorState);
- void setError(const QString &errorMessage);
+ void setError(const Error &e);
void addExecutorJobs(int jobNumber);
void removeExecutorJobs(int jobNumber);
void runAutoMoc();
diff --git a/src/lib/language/language.pri b/src/lib/language/language.pri
index 61f9c35e6..d5fc7eeb2 100644
--- a/src/lib/language/language.pri
+++ b/src/lib/language/language.pri
@@ -4,16 +4,11 @@ HEADERS += \
$$PWD/jsimports.h \
$$PWD/loader.h \
$$PWD/language.h \
- $$PWD/qbsengine.h \
$$PWD/scriptengine.h \
- $$PWD/publictypes.h \
- $$PWD/publicobjectsmap.h \
$$PWD/identifiersearch.h
SOURCES += \
$$PWD/loader.cpp \
$$PWD/language.cpp \
- $$PWD/qbsengine.cpp \
$$PWD/scriptengine.cpp \
- $$PWD/publictypes.cpp \
$$PWD/identifiersearch.cpp
diff --git a/src/lib/language/loader.h b/src/lib/language/loader.h
index 15f28475f..0f61651c1 100644
--- a/src/lib/language/loader.h
+++ b/src/lib/language/loader.h
@@ -35,9 +35,8 @@
#include <QVariantMap>
namespace qbs {
-class ProgressObserver;
-
namespace Internal {
+class ProgressObserver;
class ScriptEngine;
class Loader
diff --git a/src/lib/language/qbsengine.cpp b/src/lib/language/qbsengine.cpp
deleted file mode 100644
index 7f559f046..000000000
--- a/src/lib/language/qbsengine.cpp
+++ /dev/null
@@ -1,668 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the Qt Build Suite.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-****************************************************************************/
-
-#include "qbsengine.h"
-
-#include "publicobjectsmap.h"
-
-#include <buildgraph/artifact.h>
-#include <buildgraph/artifactcleaner.h>
-#include <buildgraph/executor.h>
-#include <language/scriptengine.h>
-#include <language/loader.h>
-#include <logging/logger.h>
-#include <logging/translator.h>
-#include <tools/platform.h>
-#include <tools/runenvironment.h>
-#include <tools/scannerpluginmanager.h>
-#include <tools/scripttools.h>
-#include <tools/settings.h>
-
-#include <QEventLoop>
-#include <QHash>
-#include <QList>
-#include <QMutex>
-#include <QMutexLocker>
-#include <QPair>
-
-namespace qbs {
-using namespace Internal;
-
-class QbsEngine::QbsEnginePrivate
-{
-public:
- QbsEnginePrivate() : observer(0) {}
-
- void loadPlugins();
- BuildProject::Ptr setupBuildProject(const ResolvedProject::ConstPtr &project);
- QVariantMap expandedBuildConfiguration(const QVariantMap &userBuildConfig);
- void buildProducts(const QList<BuildProduct::Ptr> &buildProducts,
- const BuildOptions &buildOptions);
- void buildProducts(const QList<ResolvedProduct::ConstPtr> &products,
- const BuildOptions &buildOptions, bool needsDepencencyResolving);
- void cleanBuildProducts(const QList<BuildProduct::Ptr> &buildProducts,
- const BuildOptions &buildOptions, QbsEngine::CleanType cleanType);
-
- void storeBuildGraphs(const QList<BuildProduct::Ptr> &buildProducts,
- const BuildOptions &buildOptions);
-
- ScriptEngine engine;
- ProgressObserver *observer;
- QList<ResolvedProject::Ptr> resolvedProjects;
- QList<BuildProject::Ptr> buildProjects;
- Settings settings;
- PublicObjectsMap publicObjectsMap;
-
- // Potentially stored temporarily between calls to setupResolvedProject() and setupBuildProject().
- QHash<ResolvedProject::ConstPtr, BuildProject::Ptr> discardedBuildProjects;
-
-private:
- QVariantMap createBuildConfiguration(const QVariantMap &userBuildConfig);
-
- // First: Unexpanded config (coming in via the API), second: fully expanded config
- typedef QPair<QVariantMap, QVariantMap> BuildConfigPair;
- QList<BuildConfigPair> m_buildConfigurations;
-
- static bool pluginsLoaded;
- static QMutex pluginsLoadedMutex;
-};
-
-bool QbsEngine::QbsEnginePrivate::pluginsLoaded = false;
-QMutex QbsEngine::QbsEnginePrivate::pluginsLoadedMutex;
-
-
-/*!
- * \class QbsEngine
- * \brief The \c QbsEngine class is the central facility of the qbs API.
- * It offers services such as building a project and accessing its structure.
- */
-
-/*!
- * \fn QbsEngine::QbsEngine()
- * \brief Creates a \c QbsEngine object.
- */
-QbsEngine::QbsEngine() : d(new QbsEnginePrivate)
-{
- d->loadPlugins();
-}
-
-/*!
- * \fn QbsEngine::~QbsEngine()
- * \brief Destroys a \c QbsEngine object.
- */
-QbsEngine::~QbsEngine()
-{
- delete d;
-}
-
-/*!
- * \brief Sets an observer to be used in subsequent operations.
- */
-void QbsEngine::setProgressObserver(ProgressObserver *observer)
-{
- d->observer = observer;
-}
-
-/*!
- * \brief Reads a project from a source file.
- * The \a projectFilePath parameter is the path to the project file, typically ending in ".qbs".
- * The \a buildConfig parameter is the set of properties used for resolving the project. Note that
- * calling this function with the same \a projectFilePath and a different \a buildConfig will
- * result in two distinct projects, since the different properties will potentially cause the
- * bindings in the project file to evaluate to different values.
- * The \a buildRoot parameter is the base directory for building the project. It will be used
- * to derive the actual build directory and is required here because the project information might
- * already exist on disk, in which case it will be available faster. If you know that the project
- * has never been built and you do not plan to do so later, \a buildRoot can be an arbitrary string.
- * The return value is the unique id of the resolved project, which can then be used for
- * services related to the project.
- * This function will throw qbs::Error if something goes wrong.
- * \sa Error
- */
-Project::Id QbsEngine::setupProject(const QString &projectFilePath, const QVariantMap &_buildConfig,
- const QString &buildRoot)
-{
- const QVariantMap buildConfig = d->expandedBuildConfiguration(_buildConfig);
- QScopedPointer<BuildGraph> buildGraph(new BuildGraph(&d->engine));
- buildGraph->setProgressObserver(d->observer);
- const BuildProject::LoadResult loadResult = BuildProject::load(projectFilePath,
- buildGraph.data(), buildRoot, buildConfig, d->settings.searchPaths());
-
- BuildProject::Ptr bProject;
- ResolvedProject::Ptr rProject;
- if (loadResult.loadedProject)
- buildGraph.take();
- if (!loadResult.discardLoadedProject)
- bProject = loadResult.loadedProject;
- if (bProject) {
- d->buildProjects << bProject;
- rProject = bProject->resolvedProject();
- } else {
- if (loadResult.changedResolvedProject) {
- rProject = loadResult.changedResolvedProject;
- } else {
- Loader loader(&d->engine);
- loader.setSearchPaths(d->settings.searchPaths());
- loader.setProgressObserver(d->observer);
- rProject = loader.loadProject(projectFilePath, buildRoot, buildConfig);
- }
- if (rProject->products.isEmpty())
- throw Error(QString("'%1' does not contain products.").arg(projectFilePath));
- if (loadResult.loadedProject)
- d->discardedBuildProjects.insert(rProject, loadResult.loadedProject);
- }
-
- // copy the environment from the platform config into the project's config
- const QVariantMap platformEnvironment = buildConfig.value("environment").toMap();
- rProject->platformEnvironment = platformEnvironment;
-
- qbsDebug("for %s:", qPrintable(rProject->id()));
- foreach (const ResolvedProduct::ConstPtr &p, rProject->products) {
- qbsDebug(" - [%s] %s as %s"
- ,qPrintable(p->fileTags.join(", "))
- ,qPrintable(p->name)
- ,qPrintable(p->project->id())
- );
- }
- qbsDebug("");
-
- d->resolvedProjects << rProject;
- const Project::Id id(quintptr(rProject.data()));
- d->publicObjectsMap.insertProject(id, rProject);
- return id;
-}
-
-/*!
- * \brief Builds a number of projects set up earlier via setupProject().
- * Building potentially happens in parallel. If something goes wrong, qbs::Error is thrown.
- * \sa QbsEngine::setupProject()
- * \sa Error
- */
-void QbsEngine::buildProjects(const QList<Project::Id> &projectIds, const BuildOptions &buildOptions)
-{
- QList<ResolvedProduct::ConstPtr> products;
- foreach (const Project::Id projectId, projectIds) {
- const ResolvedProject::ConstPtr resolvedProject = d->publicObjectsMap.project(projectId);
- if (!resolvedProject)
- throw Error(Tr::tr("Cannot build: No such project."));
- foreach (const ResolvedProduct::ConstPtr &product, resolvedProject->products)
- products << product;
- }
- d->buildProducts(products, buildOptions, false);
-}
-
-static QList<Project::Id> projectListToIdList(const QList<Project> &projects)
-{
- QList<Project::Id> projectIds;
- foreach (const Project &project, projects)
- projectIds << project.id();
- return projectIds;
-}
-
-/*!
- * \brief Convenience function taking a list of projects instead of ids.
- * \sa QbsEngine::buildProjects(const QList<Project::Id> &, const BuildOptions &)
- */
-void QbsEngine::buildProjects(const QList<Project> &projects, const BuildOptions &buildOptions)
-{
- buildProjects(projectListToIdList(projects), buildOptions);
-}
-
-/*!
- * \brief Convenience function for building a single project.
- * \sa QbsEngine::buildProjects(const QList<Project::Id> &, const BuildOptions &)
- */
-void QbsEngine::buildProject(Project::Id projectId, const BuildOptions &buildOptions)
-{
- buildProjects(QList<Project::Id>() << projectId, buildOptions);
-}
-
-/*!
- * \brief Builds a number of products.
- * Use this function if you want to build only a subset of a project's products. If any of
- * \a products depend on other products not listed in \a products, those will be added.
- * This function will throw qbs::Error if something goes wrong.
- * \sa Error
- */
-void QbsEngine::buildProducts(const QList<Product> &products, const BuildOptions &buildOptions)
-{
- QList<ResolvedProduct::ConstPtr> resolvedProducts;
- foreach (const Product &product, products) {
- const ResolvedProduct::ConstPtr resolvedProduct = d->publicObjectsMap.product(product.id());
- if (!resolvedProduct)
- throw Error(Tr::tr("Cannot build: No such product."));
- resolvedProducts << resolvedProduct;
- }
- d->buildProducts(resolvedProducts, buildOptions, true);
-}
-
-/*!
- * \brief Convenience function for building a single product.
- * \sa QbsEngine::buildProducts(const QList<Product> &, const BuildOptions &)
- */
-void QbsEngine::buildProduct(const Product &product, const BuildOptions &buildOptions)
-{
- buildProducts(QList<Product>() << product, buildOptions);
-}
-
-/*!
- * \enum QbsEngine::CleanType
- * This enum type specifies which kind of build artifacts to remove.
- * \value CleanupAll Indicates that all files created by the build process should be removed.
- * \value CleanupTemporaries Indicates that only intermediate build artifacts should be removed.
- * If, for example, the product to clean up for is a Linux shared library, the .so file
- * would be left on the disk, but the .o files would be removed.
- */
-
-/*!
- * \brief Removes the build artifacts of the given projects.
- * This function will throw qbs::Error if something goes wrong.
- * \sa Error
- */
-void QbsEngine::cleanProjects(const QList<Project::Id> &projectIds,
- const BuildOptions &buildOptions, CleanType cleanType)
-{
- QList<BuildProduct::Ptr> products;
- foreach (const Project::Id id, projectIds) {
- const ResolvedProject::ConstPtr rProject = d->publicObjectsMap.project(id);
- if (!rProject)
- throw Error(Tr::tr("Cleaning up failed: Project not found."));
- const BuildProject::ConstPtr bProject = d->setupBuildProject(rProject);
- foreach (const BuildProduct::Ptr &product, bProject->buildProducts())
- products << product;
- }
- d->cleanBuildProducts(products, buildOptions, cleanType);
-}
-
-/*!
- * \brief Convenience function taking a list of projects instead of ids.
- * \sa QbsEngine::cleanProjects(const QList<Project::Id> &, const BuildOptions &, CleanType)
- */
-void QbsEngine::cleanProjects(const QList<Project> &projects, const BuildOptions &buildOptions,
- CleanType cleanType)
-{
- cleanProjects(projectListToIdList(projects), buildOptions, cleanType);
-}
-
-/*!
- * Convenience function taking a single project id.
- * \sa QbsEngine::cleanProjects(const QList<Project::Id> &, const BuildOptions &, QbsEngine::CleanType)
- */
-void QbsEngine::cleanProject(Project::Id projectId, const BuildOptions &buildOptions,
- QbsEngine::CleanType cleanType)
-{
- cleanProjects(QList<Project::Id>() << projectId, buildOptions, cleanType);
-}
-
-/*!
- * \brief Removes the build artifacts of the given products.
- * This function will throw qbs::Error if something goes wrong.
- * \sa Error
- */
-void QbsEngine::cleanProducts(const QList<Product> &products, const BuildOptions &buildOptions,
- QbsEngine::CleanType cleanType)
-{
- QList<BuildProduct::Ptr> buildProducts;
- foreach (const Product &product, products) {
- const ResolvedProduct::ConstPtr rProduct = d->publicObjectsMap.product(product.id());
- if (!rProduct)
- throw Error(Tr::tr("Cleaning up failed: Product not found."));
- const ResolvedProject::ConstPtr rProject = rProduct->project.toStrongRef();
- const BuildProject::ConstPtr bProject = d->setupBuildProject(rProject);
- foreach (const BuildProduct::Ptr &bProduct, bProject->buildProducts()) {
- if (bProduct->rProduct == rProduct) {
- buildProducts << bProduct;
- break;
- }
- }
- }
- d->cleanBuildProducts(buildProducts, buildOptions, cleanType);
-}
-
-/*!
- * \brief Convenience function taking a single product
- * \sa QbsEngine::cleanProducts(const QList<Product> &, const BuildOptions &, QbsEngine::CleanType)
- */
-void QbsEngine::cleanProduct(const Product &product, const BuildOptions &buildOptions, QbsEngine::CleanType cleanType)
-{
- cleanProducts(QList<Product>() << product, buildOptions, cleanType);
-}
-
-/*!
- * \brief Retrieves information for a given project.
- * Call this function if you need insight into the project structure, e.g. because you want to know
- * which products or files are in it. If you just want to build it, the id is enough.
- * This function will throw qbs::Error if something goes wrong.
- * \sa Error
- */
-Project QbsEngine::retrieveProject(Project::Id projectId) const
-{
- const ResolvedProject::ConstPtr resolvedProject = d->publicObjectsMap.project(projectId);
- if (!resolvedProject)
- throw Error(Tr::tr("Cannot retrieve project: No such project."));
- Project project(projectId);
- project.m_qbsFilePath = resolvedProject->qbsFile;
- foreach (const ResolvedProduct::Ptr &resolvedProduct, resolvedProject->products) {
- Product product(Product::Id(quintptr(resolvedProduct.data())));
- product.m_name = resolvedProduct->name;
- product.m_qbsFilePath = resolvedProduct->qbsFile;
- product.m_qbsLine = resolvedProduct->qbsLine;
- product.m_fileTags = resolvedProduct->fileTags;
- foreach (const ResolvedGroup::Ptr &resolvedGroup, resolvedProduct->groups) {
- Group group(Group::Id(quintptr(resolvedGroup.data())));
- group.m_name = resolvedGroup->name;
- group.m_qbsLine = resolvedGroup->qbsLine;
- foreach (const SourceArtifact::ConstPtr &sa, resolvedGroup->files)
- group.m_filePaths << sa->absoluteFilePath;
- if (resolvedGroup->wildcards) {
- foreach (const SourceArtifact::ConstPtr &sa, resolvedGroup->wildcards->files)
- group.m_expandedWildcards << sa->absoluteFilePath;
- }
- group.m_properties = resolvedGroup->properties->value();
- product.m_groups << group;
- d->publicObjectsMap.insertGroup(group.m_id, resolvedGroup);
- }
- project.m_products << product;
- d->publicObjectsMap.insertProduct(product.m_id, resolvedProduct);
- }
- return project;
-}
-
-RunEnvironment QbsEngine::getRunEnvironment(const Product &product,
- const QProcessEnvironment &environment)
-{
- return RunEnvironment(&d->engine, product, d->publicObjectsMap, environment);
-}
-
-/*!
- * \brief Returns the file path of the executable associated with the given product.
- * If the product is not an application, an empty string is returned.
- * This function will throw qbs::Error if something goes wrong.
- * \sa Error
- */
-QString QbsEngine::targetExecutable(const Product &product)
-{
- const ResolvedProduct::ConstPtr resolvedProduct = d->publicObjectsMap.product(product.id());
- if (!resolvedProduct)
- throw Error(Tr::tr("Unknown product."));
- if (!resolvedProduct->fileTags.contains(QLatin1String("application")))
- return QString();
- ResolvedProject::ConstPtr resolvedProject;
- foreach (const ResolvedProject::ConstPtr &p, d->resolvedProjects) {
- if (p == resolvedProduct->project) {
- resolvedProject = p;
- break;
- }
- }
- if (!resolvedProject)
- throw Error(Tr::tr("Unknown product '%1'.").arg(resolvedProduct->name));
-
- const BuildProject::ConstPtr buildProject = d->setupBuildProject(resolvedProject);
- Q_ASSERT(buildProject->resolvedProject() == resolvedProject);
- BuildProduct::ConstPtr buildProduct;
- foreach (const BuildProduct::ConstPtr &bp, buildProject->buildProducts()) {
- if (bp->rProduct == resolvedProduct) {
- buildProduct = bp;
- break;
- }
- }
- Q_ASSERT(buildProduct);
-
- foreach (const Artifact * const artifact, buildProduct->targetArtifacts) {
- if (artifact->fileTags.contains(QLatin1String("application")))
- return artifact->filePath();
- }
- return QString();
-}
-
-void QbsEngine::QbsEnginePrivate::loadPlugins()
-{
- QMutexLocker locker(&pluginsLoadedMutex);
- if (pluginsLoaded)
- return;
-
- QStringList pluginPaths;
- foreach (const QString &pluginPath, settings.pluginPaths()) {
- if (!FileInfo::exists(pluginPath)) {
- qbsWarning() << Tr::tr("Plugin path '%1' does not exist.")
- .arg(QDir::toNativeSeparators(pluginPath));
- } else {
- pluginPaths << pluginPath;
- }
- }
- ScannerPluginManager::instance()->loadPlugins(pluginPaths);
-
- pluginsLoaded = true;
-}
-
-BuildProject::Ptr QbsEngine::QbsEnginePrivate::setupBuildProject(const ResolvedProject::ConstPtr &project)
-{
- foreach (const BuildProject::Ptr &buildProject, buildProjects) {
- if (buildProject->resolvedProject() == project)
- return buildProject;
- }
-
- ResolvedProject::Ptr mutableRProject;
- foreach (const ResolvedProject::Ptr &p, resolvedProjects) {
- if (p == project) {
- mutableRProject = p;
- break;
- }
- }
- if (!mutableRProject)
- throw Error(Tr::tr("Unknown project."));
-
- TimedActivityLogger resolveLogger(QLatin1String("Resolving build project"));
- BuildGraph * const buildGraph = new BuildGraph(&engine);
- buildGraph->setProgressObserver(observer);
- const BuildProject::Ptr buildProject = buildGraph->resolveProject(mutableRProject);
- const QHash<ResolvedProject::ConstPtr, BuildProject::Ptr>::Iterator it
- = discardedBuildProjects.find(project);
- if (it != discardedBuildProjects.end()) {
- buildProject->rescueDependencies(it.value());
- discardedBuildProjects.erase(it);
- }
-
- buildProjects << buildProject;
- return buildProject;
-}
-
-QVariantMap QbsEngine::QbsEnginePrivate::expandedBuildConfiguration(const QVariantMap &userBuildConfig)
-{
- foreach (const QbsEnginePrivate::BuildConfigPair &configPair, m_buildConfigurations) {
- if (configPair.first == userBuildConfig)
- return configPair.second;
- }
- const QbsEnginePrivate::BuildConfigPair configPair
- = qMakePair(userBuildConfig, createBuildConfiguration(userBuildConfig));
- m_buildConfigurations << configPair;
- return configPair.second;
-}
-
-void QbsEngine::QbsEnginePrivate::buildProducts(const QList<BuildProduct::Ptr> &buildProducts,
- const BuildOptions &buildOptions)
-{
- Executor executor;
- QEventLoop execLoop;
- QObject::connect(&executor, SIGNAL(finished()), &execLoop, SLOT(quit()), Qt::QueuedConnection);
- QObject::connect(&executor, SIGNAL(error()), &execLoop, SLOT(quit()), Qt::QueuedConnection);
- executor.setEngine(&engine);
- executor.setProgressObserver(observer);
- executor.setBuildOptions(buildOptions);
- TimedActivityLogger buildLogger(QLatin1String("Building products"), QString(), LoggerInfo);
- executor.build(buildProducts);
- execLoop.exec();
- buildLogger.finishActivity();
- storeBuildGraphs(buildProducts, buildOptions);
- if (executor.buildResult() != Executor::SuccessfulBuild)
- throw Error(Tr::tr("Build failed."));
-}
-
-void QbsEngine::QbsEnginePrivate::buildProducts(const QList<ResolvedProduct::ConstPtr> &products,
- const BuildOptions &buildOptions, bool needsDepencencyResolving)
-{
- // Make sure all products are set up first.
- QSet<ResolvedProject::ConstPtr> rProjects;
- foreach (const ResolvedProduct::ConstPtr &product, products)
- rProjects << product->project.toStrongRef();
- foreach (const ResolvedProject::ConstPtr &rProject, rProjects)
- setupBuildProject(rProject);
-
- // Gather build products.
- QList<BuildProduct::Ptr> productsToBuild;
- foreach (const ResolvedProduct::ConstPtr &rProduct, products) {
- foreach (const BuildProject::ConstPtr &buildProject, buildProjects) {
- foreach (const BuildProduct::Ptr &buildProduct, buildProject->buildProducts()) {
- if (buildProduct->rProduct == rProduct)
- productsToBuild << buildProduct;
- }
- }
- }
-
- if (needsDepencencyResolving) {
- for (int i = 0; i < productsToBuild.count(); ++i) {
- const BuildProduct::ConstPtr &product = productsToBuild.at(i);
- foreach (const BuildProduct::Ptr &dependency, product->dependencies)
- productsToBuild << dependency;
- }
- }
-
- productsToBuild = productsToBuild.toSet().toList(); // make products unique
- buildProducts(productsToBuild, buildOptions);
-}
-
-void QbsEngine::QbsEnginePrivate::cleanBuildProducts(const QList<BuildProduct::Ptr> &buildProducts,
- const BuildOptions &buildOptions, QbsEngine::CleanType cleanType)
-{
- try {
- ArtifactCleaner cleaner;
- cleaner.cleanup(buildProducts, cleanType == QbsEngine::CleanupAll, buildOptions);
- } catch (const Error &) {
- storeBuildGraphs(buildProducts, buildOptions);
- throw;
- }
- storeBuildGraphs(buildProducts, buildOptions);
-}
-
-void QbsEngine::QbsEnginePrivate::storeBuildGraphs(const QList<BuildProduct::Ptr> &buildProducts,
- const BuildOptions &buildOptions)
-{
- if (buildOptions.dryRun)
- return;
- QSet<BuildProject *> buildProjects;
- foreach (const BuildProduct::ConstPtr &bProduct, buildProducts)
- buildProjects += bProduct->project;
- foreach (const BuildProject * const bProject, buildProjects)
- bProject->store();
-}
-
-QVariantMap QbsEngine::QbsEnginePrivate::createBuildConfiguration(const QVariantMap &userBuildConfig)
-{
- QHash<QString, Platform::Ptr > platforms = Platform::platforms();
- if (platforms.isEmpty())
- throw Error(Tr::tr("No platforms configured. You must run 'qbs platforms probe' first."));
-
- QVariantMap expandedConfig = userBuildConfig;
-
- // Fill in buildCfg in this order (making sure not to overwrite a key already set by a previous stage)
- // 1) Things specified on command line (already in buildCfg at this point)
- // 2) Everything from the profile key (in reverse order)
- // 3) Everything from the platform
- // 4) Any remaining keys from modules keyspace
- QString profileName = expandedConfig.value("qbs.profile").toString();
- if (profileName.isNull()) {
- profileName = settings.value("profile").toString();
- if (profileName.isNull())
- throw Error(Tr::tr("No profile given.\n"
- "Either set the configuration value 'profile' to a valid profile's name\n"
- "or specify the profile with the command line parameter 'profile:name'."));
- expandedConfig.insert("qbs.profile", profileName);
- }
-
- // (2)
- const QString profileGroup = QString("profiles/%1").arg(profileName);
- const QStringList profileKeys = settings.allKeysWithPrefix(profileGroup);
- if (profileKeys.isEmpty())
- throw Error(Tr::tr("Unknown profile '%1'.").arg(profileName));
- foreach (const QString &profileKey, profileKeys) {
- QString fixedKey(profileKey);
- fixedKey.replace(QChar('/'), QChar('.'));
- if (!expandedConfig.contains(fixedKey))
- expandedConfig.insert(fixedKey, settings.value(profileGroup + "/" + profileKey));
- }
-
- // (3) Need to make sure we have a value for qbs.platform before going any further
- QVariant platformName = expandedConfig.value("qbs.platform");
- if (!platformName.isValid()) {
- platformName = settings.moduleValue("qbs/platform", profileName);
- if (!platformName.isValid())
- throw Error(Tr::tr("No platform given and no default set."));
- expandedConfig.insert("qbs.platform", platformName);
- }
- Platform::Ptr platform = platforms.value(platformName.toString());
- if (platform.isNull())
- throw Error(Tr::tr("Unknown platform '%1'.").arg(platformName.toString()));
- foreach (const QString &key, platform->settings.allKeys()) {
- if (key.startsWith(Platform::internalKey()))
- continue;
- QString fixedKey = key;
- int idx = fixedKey.lastIndexOf(QChar('/'));
- if (idx > 0)
- fixedKey[idx] = QChar('.');
- if (!expandedConfig.contains(fixedKey))
- expandedConfig.insert(fixedKey, platform->settings.value(key));
- }
- // Now finally do (4)
- foreach (const QString &defaultKey, settings.allKeysWithPrefix("modules")) {
- QString fixedKey(defaultKey);
- fixedKey.replace(QChar('/'), QChar('.'));
- if (!expandedConfig.contains(fixedKey))
- expandedConfig.insert(fixedKey, settings.value(QString("modules/") + defaultKey));
- }
-
- if (!expandedConfig.value("qbs.buildVariant").isValid())
- throw Error(Tr::tr("Property 'qbs.buildVariant' missing in build configuration."));
-
- foreach (const QString &property, expandedConfig.keys()) {
- QStringList nameElements = property.split('.');
- if (nameElements.count() == 1 && nameElements.first() != "project") // ### Still need this because platform doesn't supply fully-qualified properties (yet), need to fix this
- nameElements.prepend("qbs");
- if (nameElements.count() > 2) { // ### workaround for submodules being represented internally as a single module of name "module/submodule" rather than two nested modules "module" and "submodule"
- QStringList newElements(QStringList(nameElements.mid(0, nameElements.count()-1)).join("/"));
- newElements.append(nameElements.last());
- nameElements = newElements;
- }
- setConfigProperty(expandedConfig, nameElements, expandedConfig.value(property));
- expandedConfig.remove(property);
- }
-
- return expandedConfig;
-}
-
-} // namespace qbs
diff --git a/src/lib/language/qbsengine.h b/src/lib/language/qbsengine.h
deleted file mode 100644
index eb8eba7a9..000000000
--- a/src/lib/language/qbsengine.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the Qt Build Suite.
-**
-** 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 QBSINTERFACE_H
-#define QBSINTERFACE_H
-
-#include "publictypes.h"
-
-#include <QList>
-#include <QStringList>
-
-QT_BEGIN_NAMESPACE
-class QProcessEnvironment;
-QT_END_NAMESPACE
-
-namespace qbs {
-class BuildOptions;
-class RunEnvironment;
-class ProgressObserver;
-
-class QbsEngine
-{
- Q_DISABLE_COPY(QbsEngine)
-public:
- QbsEngine();
- ~QbsEngine();
-
- void setProgressObserver(ProgressObserver *observer);
-
- Project::Id setupProject(const QString &projectFilePath, const QVariantMap &buildConfig,
- const QString &buildRoot);
- void buildProjects(const QList<Project::Id> &projectIds, const BuildOptions &buildOptions);
- void buildProjects(const QList<Project> &projects, const BuildOptions &buildOptions);
- void buildProject(Project::Id projectId, const BuildOptions &buildOptions);
- void buildProducts(const QList<Product> &products, const BuildOptions &buildOptions);
- void buildProduct(const Product &product, const BuildOptions &buildOptions);
-
- enum CleanType { CleanupAll, CleanupTemporaries };
- void cleanProjects(const QList<Project::Id> &projectIds, const BuildOptions &buildOptions,
- CleanType cleanType);
- void cleanProjects(const QList<Project> &projects, const BuildOptions &buildOptions,
- CleanType cleanType);
- void cleanProject(Project::Id projectId, const BuildOptions &buildOptions, CleanType cleanType);
- void cleanProducts(const QList<Product> &products, const BuildOptions &buildOptions,
- CleanType cleanType);
- void cleanProduct(const Product &product, const BuildOptions &buildOptions,
- CleanType cleanType);
-
- Project retrieveProject(Project::Id projectId) const;
-
- RunEnvironment getRunEnvironment(const Product &product,
- const QProcessEnvironment &environment);
-
- QString targetExecutable(const Product &product);
-
-private:
- class QbsEnginePrivate;
- QbsEnginePrivate * const d;
-};
-
-} // namespace qbs
-
-#endif
diff --git a/src/lib/lib.pro b/src/lib/lib.pro
index 21a256185..65c17f1c1 100644
--- a/src/lib/lib.pro
+++ b/src/lib/lib.pro
@@ -10,12 +10,13 @@ DEFINES += QT_CREATOR QML_BUILD_STATIC_LIB # needed for QmlJS
win32:CONFIG(debug, debug|release):TARGET = $${TARGET}d
include(../../qbs_version.pri)
-include(jsextensions/jsextensions.pri)
-include(tools/tools.pri)
-include(parser/parser.pri)
+include(api/api.pri)
include(buildgraph/buildgraph.pri)
+include(jsextensions/jsextensions.pri)
include(language/language.pri)
include(logging/logging.pri)
+include(parser/parser.pri)
+include(tools/tools.pri)
HEADERS += \
qbs.h
diff --git a/src/lib/qbs.h b/src/lib/qbs.h
index 11ddbbcf8..0e5c686dd 100644
--- a/src/lib/qbs.h
+++ b/src/lib/qbs.h
@@ -29,12 +29,12 @@
#ifndef QBS_H
#define QBS_H
-#include "language/publictypes.h"
-#include "language/qbsengine.h"
+#include <api/jobs.h>
+#include <api/project.h>
+#include "api/projectdata.h"
#include "logging/logger.h"
#include "tools/buildoptions.h"
#include "tools/error.h"
-#include "tools/progressobserver.h"
#include "tools/settings.h"
#endif // QBS_H
diff --git a/src/lib/tools/buildoptions.cpp b/src/lib/tools/buildoptions.cpp
index 08b8d8b07..9fcf02705 100644
--- a/src/lib/tools/buildoptions.cpp
+++ b/src/lib/tools/buildoptions.cpp
@@ -60,9 +60,9 @@ BuildOptions::BuildOptions()
/*!
* \variable BuildOptions::dryRun
* \brief if true, qbs will not actually execute any commands, but just show what would happen
- * Note that the next call to build() on the same QbsEngine object will do nothing, since the
+ * Note that the next call to build() on the same \c Project object will do nothing, since the
* internal state needs to be updated the same way as if an actual build has happened. You'll
- * need to use a new QbsEngine object to do a real build.
+ * need to create a new \c Project object to do a real build.
*/
/*!
diff --git a/src/lib/tools/progressobserver.cpp b/src/lib/tools/progressobserver.cpp
index df6563f0c..99ce0682a 100644
--- a/src/lib/tools/progressobserver.cpp
+++ b/src/lib/tools/progressobserver.cpp
@@ -29,6 +29,7 @@
#include "progressobserver.h"
namespace qbs {
+namespace Internal {
/*!
* \class ProgressObserver
@@ -90,4 +91,5 @@ void ProgressObserver::setFinished()
setProgressValue(maximum());
}
+} // namespace Internal
} // namespace qbs
diff --git a/src/lib/tools/progressobserver.h b/src/lib/tools/progressobserver.h
index 2653fbfac..f5733194c 100644
--- a/src/lib/tools/progressobserver.h
+++ b/src/lib/tools/progressobserver.h
@@ -36,6 +36,7 @@ class QString;
QT_END_NAMESPACE
namespace qbs {
+namespace Internal {
class ProgressObserver
{
@@ -54,6 +55,7 @@ public:
void setFinished();
};
+} // namespace Internal
} // namespace qbs
#endif // PROGRESSOBSERVER_H
diff --git a/src/lib/tools/tools.pri b/src/lib/tools/tools.pri
index 996ab1215..be0f09b37 100644
--- a/src/lib/tools/tools.pri
+++ b/src/lib/tools/tools.pri
@@ -13,7 +13,6 @@ HEADERS += \
$$PWD/progressobserver.h \
$$PWD/hostosinfo.h \
$$PWD/buildoptions.h \
- $$PWD/runenvironment.h \
$$PWD/persistentobject.h \
$$PWD/weakpointer.h
@@ -25,7 +24,6 @@ SOURCES += \
$$PWD/scannerpluginmanager.cpp \
$$PWD/scripttools.cpp \
$$PWD/settings.cpp \
- $$PWD/runenvironment.cpp \
$$PWD/progressobserver.cpp \
$$PWD/buildoptions.cpp