aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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