diff options
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 ¤tJobEffort = 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 |