summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@digia.com>2013-04-08 18:18:16 +0200
committerJoerg Bornemann <joerg.bornemann@digia.com>2013-04-12 14:19:12 +0200
commit1d9c080428dcd5e8c1d08598d6542d87f1ca1819 (patch)
tree38282acbb95c921595dffec93a6dd99aaa61c24f
parentf17fd5d56a758a28ef5da04b2be83ed558f7cba6 (diff)
downloadqbs-1d9c080428dcd5e8c1d08598d6542d87f1ca1819.tar.gz
Remove structural redundancy in project and product classes.
We had two project classes, each holding a list of products that was structurally identical, except that no BuildProduct object existed for a disabled ResolvedProduct. The same kind of duplication also happened for product dependencies. This patch gets rid of these parallel structures. BuildProject and BuildProduct are largely being demoted to data holders and are aggregated by ResolvedProject and ResolvedProduct, respectively. The resulting project structure should be easier to understand and maintain. Change-Id: I68beef60b9e0d62258f6a8337c9015864e18bd80 Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
-rw-r--r--src/lib/api/internaljobs.cpp58
-rw-r--r--src/lib/api/internaljobs.h23
-rw-r--r--src/lib/api/jobs.cpp12
-rw-r--r--src/lib/api/jobs.h11
-rw-r--r--src/lib/api/project.cpp89
-rw-r--r--src/lib/api/project.h5
-rw-r--r--src/lib/buildgraph/artifact.cpp27
-rw-r--r--src/lib/buildgraph/artifact.h16
-rw-r--r--src/lib/buildgraph/artifactcleaner.cpp19
-rw-r--r--src/lib/buildgraph/artifactcleaner.h5
-rw-r--r--src/lib/buildgraph/artifactvisitor.cpp14
-rw-r--r--src/lib/buildgraph/artifactvisitor.h6
-rw-r--r--src/lib/buildgraph/automoc.cpp21
-rw-r--r--src/lib/buildgraph/automoc.h3
-rw-r--r--src/lib/buildgraph/buildgraph.cpp458
-rw-r--r--src/lib/buildgraph/buildgraph.h54
-rw-r--r--src/lib/buildgraph/buildgraph.pri8
-rw-r--r--src/lib/buildgraph/buildproduct.cpp241
-rw-r--r--src/lib/buildgraph/buildproject.cpp803
-rw-r--r--src/lib/buildgraph/buildproject.h166
-rw-r--r--src/lib/buildgraph/cycledetector.cpp6
-rw-r--r--src/lib/buildgraph/cycledetector.h2
-rw-r--r--src/lib/buildgraph/executor.cpp51
-rw-r--r--src/lib/buildgraph/executor.h9
-rw-r--r--src/lib/buildgraph/executorjob.cpp5
-rw-r--r--src/lib/buildgraph/executorjob.h5
-rw-r--r--src/lib/buildgraph/forward_decls.h10
-rw-r--r--src/lib/buildgraph/inputartifactscanner.cpp35
-rw-r--r--src/lib/buildgraph/jscommandexecutor.cpp7
-rw-r--r--src/lib/buildgraph/processcommandexecutor.cpp5
-rw-r--r--src/lib/buildgraph/productbuilddata.cpp114
-rw-r--r--src/lib/buildgraph/productbuilddata.h (renamed from src/lib/buildgraph/buildproduct.h)32
-rw-r--r--src/lib/buildgraph/productinstaller.cpp20
-rw-r--r--src/lib/buildgraph/productinstaller.h6
-rw-r--r--src/lib/buildgraph/projectbuilddata.cpp380
-rw-r--r--src/lib/buildgraph/projectbuilddata.h105
-rw-r--r--src/lib/buildgraph/rulesapplicator.cpp45
-rw-r--r--src/lib/buildgraph/rulesapplicator.h4
-rw-r--r--src/lib/buildgraph/timestampsupdater.cpp22
-rw-r--r--src/lib/buildgraph/timestampsupdater.h5
-rw-r--r--src/lib/buildgraph/tst_buildgraph.cpp25
-rw-r--r--src/lib/buildgraph/tst_buildgraph.h9
-rw-r--r--src/lib/language/language.cpp63
-rw-r--r--src/lib/language/language.h15
-rw-r--r--src/lib/lib.qbs8
-rw-r--r--src/lib/tools/persistence.cpp2
46 files changed, 1495 insertions, 1534 deletions
diff --git a/src/lib/api/internaljobs.cpp b/src/lib/api/internaljobs.cpp
index e29919fe3..b41ec17bc 100644
--- a/src/lib/api/internaljobs.cpp
+++ b/src/lib/api/internaljobs.cpp
@@ -31,8 +31,9 @@
#include "jobs.h"
#include <buildgraph/artifactcleaner.h>
-#include <buildgraph/buildproduct.h>
-#include <buildgraph/buildproject.h>
+#include <buildgraph/buildgraph.h>
+#include <buildgraph/productbuilddata.h>
+#include <buildgraph/projectbuilddata.h>
#include <buildgraph/executor.h>
#include <buildgraph/productinstaller.h>
#include <buildgraph/rulesevaluationcontext.h>
@@ -118,10 +119,10 @@ void InternalJob::cancel()
m_observer->cancel();
}
-void InternalJob::storeBuildGraph(const BuildProject *buildProject)
+void InternalJob::storeBuildGraph(const ResolvedProjectConstPtr &project)
{
try {
- buildProject->store();
+ project->store(logger());
} catch (const Error &error) {
logger().qbsWarning() << error.toString();
}
@@ -153,9 +154,9 @@ void InternalSetupProjectJob::reportError(const Error &error)
Q_ARG(Internal::InternalJob *, this));
}
-BuildProjectPtr InternalSetupProjectJob::buildProject() const
+ResolvedProjectPtr InternalSetupProjectJob::project() const
{
- return m_buildProject;
+ return m_project;
}
void InternalSetupProjectJob::start()
@@ -187,23 +188,20 @@ void InternalSetupProjectJob::execute()
{
RulesEvaluationContextPtr evalContext(new RulesEvaluationContext(logger()));
evalContext->setObserver(observer());
- BuildProjectLoader bpLoader(logger());
- const BuildProjectLoader::LoadResult loadResult = bpLoader.load(m_parameters, evalContext);
- ResolvedProjectPtr rProject;
+ BuildGraphLoader bpLoader(logger());
+ const BuildGraphLoader::LoadResult loadResult = bpLoader.load(m_parameters, evalContext);
if (!loadResult.discardLoadedProject)
- m_buildProject = loadResult.loadedProject;
- if (m_buildProject) {
- rProject = m_buildProject->resolvedProject();
- } else {
- if (loadResult.changedResolvedProject) {
- rProject = loadResult.changedResolvedProject;
+ m_project = loadResult.loadedProject;
+ if (!m_project) {
+ if (loadResult.newlyResolvedProject) {
+ m_project = loadResult.newlyResolvedProject;
} else {
Loader loader(evalContext->engine(), logger());
loader.setSearchPaths(m_parameters.searchPaths);
loader.setProgressObserver(observer());
- rProject = loader.loadProject(m_parameters);
+ m_project = loader.loadProject(m_parameters);
}
- if (rProject->products.isEmpty()) {
+ if (m_project->products.isEmpty()) {
throw Error(Tr::tr("Project '%1' does not contain products.")
.arg(m_parameters.projectFilePath));
}
@@ -212,28 +210,28 @@ void InternalSetupProjectJob::execute()
// copy the environment from the platform config into the project's config
const QVariantMap platformEnvironment
= m_parameters.buildConfiguration.value(QLatin1String("environment")).toMap();
- rProject->platformEnvironment = platformEnvironment;
+ m_project->platformEnvironment = platformEnvironment;
- logger().qbsDebug() << QString::fromLocal8Bit("for %1:").arg(rProject->id());
- foreach (const ResolvedProductConstPtr &p, rProject->products) {
+ logger().qbsDebug() << QString::fromLocal8Bit("for %1:").arg(m_project->id());
+ foreach (const ResolvedProductConstPtr &p, m_project->products) {
logger().qbsDebug() << QString::fromLocal8Bit(" - [%1] %2 as %3")
.arg(p->fileTags.toStringList().join(QLatin1String(", ")))
.arg(p->name).arg(p->project->id());
}
logger().qbsDebug() << '\n';
- if (!m_buildProject) {
+ if (!m_project->buildData) {
TimedActivityLogger resolveLogger(logger(), QLatin1String("Resolving build project"));
- m_buildProject = BuildProjectResolver(logger()).resolveProject(rProject, evalContext);
+ BuildDataResolver(logger()).resolveBuildData(m_project, evalContext);
if (loadResult.loadedProject)
- m_buildProject->rescueData(loadResult.loadedProject);
+ BuildDataResolver::rescueBuildData(loadResult.loadedProject, m_project, logger());
}
if (!m_parameters.dryRun)
- storeBuildGraph(m_buildProject.data());
+ storeBuildGraph(m_project);
// The evalutation context cannot be re-used for building, which runs in a different thread.
- m_buildProject->setEvaluationContext(RulesEvaluationContextPtr());
+ m_project->buildData->evaluationContext.clear();
}
@@ -246,7 +244,7 @@ BuildGraphTouchingJob::~BuildGraphTouchingJob()
{
}
-void BuildGraphTouchingJob::setup(const QList<BuildProductPtr> &products, bool dryRun)
+void BuildGraphTouchingJob::setup(const QList<ResolvedProductPtr> &products, bool dryRun)
{
m_products = products;
m_dryRun = dryRun;
@@ -263,7 +261,7 @@ InternalBuildJob::InternalBuildJob(const Logger &logger, QObject *parent)
{
}
-void InternalBuildJob::build(const QList<BuildProductPtr> &products,
+void InternalBuildJob::build(const QList<ResolvedProductPtr> &products,
const BuildOptions &buildOptions, const QProcessEnvironment &env)
{
setup(products, buildOptions.dryRun);
@@ -295,7 +293,7 @@ void InternalBuildJob::handleFinished()
if (m_executor->hasError())
setError(m_executor->error());
if (!products().isEmpty())
- products().first()->project->setEvaluationContext(RulesEvaluationContextPtr());
+ products().first()->project->buildData->evaluationContext.clear();
storeBuildGraph();
m_executor->deleteLater();
}
@@ -310,7 +308,7 @@ InternalCleanJob::InternalCleanJob(const Logger &logger, QObject *parent)
{
}
-void InternalCleanJob::clean(const QList<BuildProductPtr> &products, const CleanOptions &options)
+void InternalCleanJob::clean(const QList<ResolvedProductPtr> &products, const CleanOptions &options)
{
setup(products, options.dryRun);
setTimed(options.logElapsedTime);
@@ -351,7 +349,7 @@ InternalInstallJob::~InternalInstallJob()
{
}
-void InternalInstallJob::install(const QList<BuildProductPtr> &products,
+void InternalInstallJob::install(const QList<ResolvedProductPtr> &products,
const InstallOptions &options)
{
m_products = products;
diff --git a/src/lib/api/internaljobs.h b/src/lib/api/internaljobs.h
index 953977799..1e3f4db53 100644
--- a/src/lib/api/internaljobs.h
+++ b/src/lib/api/internaljobs.h
@@ -29,8 +29,7 @@
#ifndef QBS_INTERNALJOBS_H
#define QBS_INTERNALJOBS_H
-#include "projectdata.h"
-#include <buildgraph/forward_decls.h>
+#include <language/forward_decls.h>
#include <logging/logger.h>
#include <tools/buildoptions.h>
#include <tools/cleanoptions.h>
@@ -73,7 +72,7 @@ protected:
JobObserver *observer() const { return m_observer; }
void setError(const Error &error) { m_error = error; }
void setTimed(bool timed) { m_timed = timed; }
- void storeBuildGraph(const BuildProject *buildProject);
+ void storeBuildGraph(const ResolvedProjectConstPtr &project);
signals:
void finished(Internal::InternalJob *job);
@@ -99,7 +98,7 @@ public:
void resolve(const SetupProjectParameters &parameters);
void reportError(const Error &error);
- BuildProjectPtr buildProject() const;
+ ResolvedProjectPtr project() const;
private slots:
void start();
@@ -113,7 +112,7 @@ private:
QMutex m_runMutex;
QWaitCondition m_runWaitCondition;
- BuildProjectPtr m_buildProject;
+ ResolvedProjectPtr m_project;
SetupProjectParameters m_parameters;
};
@@ -122,7 +121,7 @@ class BuildGraphTouchingJob : public InternalJob
{
Q_OBJECT
public:
- const QList<BuildProductPtr> &products() const { return m_products; }
+ const QList<ResolvedProductPtr> &products() const { return m_products; }
signals:
void reportCommandDescription(const QString &highlight, const QString &message);
@@ -133,11 +132,11 @@ protected:
BuildGraphTouchingJob(const Logger &logger, QObject *parent = 0);
~BuildGraphTouchingJob();
- void setup(const QList<BuildProductPtr> &products, bool dryRun);
+ void setup(const QList<ResolvedProductPtr> &products, bool dryRun);
void storeBuildGraph();
private:
- QList<BuildProductPtr> m_products;
+ QList<ResolvedProductPtr> m_products;
bool m_dryRun;
};
@@ -148,7 +147,7 @@ class InternalBuildJob : public BuildGraphTouchingJob
public:
InternalBuildJob(const Logger &logger, QObject *parent = 0);
- void build(const QList<BuildProductPtr> &products, const BuildOptions &buildOptions,
+ void build(const QList<ResolvedProductPtr> &products, const BuildOptions &buildOptions,
const QProcessEnvironment &env);
private slots:
@@ -166,7 +165,7 @@ class InternalCleanJob : public BuildGraphTouchingJob
public:
InternalCleanJob(const Logger &logger, QObject *parent = 0);
- void clean(const QList<BuildProductPtr> &products, const CleanOptions &options);
+ void clean(const QList<ResolvedProductPtr> &products, const CleanOptions &options);
private slots:
void start();
@@ -187,7 +186,7 @@ public:
InternalInstallJob(const Logger &logger, QObject *parent = 0);
~InternalInstallJob();
- void install(const QList<BuildProductPtr> &products, const InstallOptions &options);
+ void install(const QList<ResolvedProductPtr> &products, const InstallOptions &options);
private slots:
void handleFinished();
@@ -197,7 +196,7 @@ private:
void doInstall();
private:
- QList<BuildProductPtr> m_products;
+ QList<ResolvedProductPtr> m_products;
InstallOptions m_options;
};
diff --git a/src/lib/api/jobs.cpp b/src/lib/api/jobs.cpp
index a7b8f87c9..49f72bf6a 100644
--- a/src/lib/api/jobs.cpp
+++ b/src/lib/api/jobs.cpp
@@ -30,11 +30,7 @@
#include "internaljobs.h"
#include "project.h"
-#include <buildgraph/buildproject.h>
-#include <buildgraph/rulesevaluationcontext.h>
#include <language/language.h>
-#include <language/scriptengine.h>
-#include <tools/setupprojectparameters.h>
#include <tools/qbsassert.h>
namespace qbs {
@@ -193,7 +189,7 @@ Project SetupProjectJob::project() const
{
const InternalSetupProjectJob * const job
= qobject_cast<InternalSetupProjectJob *>(internalJob());
- return Project(job->buildProject(), job->logger());
+ return Project(job->project(), job->logger());
}
void SetupProjectJob::resolve(const SetupProjectParameters &parameters)
@@ -253,7 +249,7 @@ BuildJob::BuildJob(const Logger &logger, QObject *parent)
connect(job, SIGNAL(reportWarning(qbs::Error)), this, SIGNAL(reportWarning(qbs::Error)));
}
-void BuildJob::build(const QList<BuildProductPtr> &products, const BuildOptions &options,
+void BuildJob::build(const QList<ResolvedProductPtr> &products, const BuildOptions &options,
const QProcessEnvironment &env)
{
qobject_cast<InternalBuildJob *>(internalJob())->build(products, options, env);
@@ -270,7 +266,7 @@ CleanJob::CleanJob(const Logger &logger, QObject *parent)
{
}
-void CleanJob::clean(const QList<BuildProductPtr> &products, const qbs::CleanOptions &options)
+void CleanJob::clean(const QList<ResolvedProductPtr> &products, const qbs::CleanOptions &options)
{
qobject_cast<InternalCleanJob *>(internalJob())->clean(products, options);
}
@@ -285,7 +281,7 @@ InstallJob::InstallJob(const Logger &logger, QObject *parent)
{
}
-void InstallJob::install(const QList<BuildProductPtr> &products, const InstallOptions &options)
+void InstallJob::install(const QList<ResolvedProductPtr> &products, const InstallOptions &options)
{
qobject_cast<InternalInstallJob *>(internalJob())->install(products, options);
}
diff --git a/src/lib/api/jobs.h b/src/lib/api/jobs.h
index 997720c7b..baacfdb2e 100644
--- a/src/lib/api/jobs.h
+++ b/src/lib/api/jobs.h
@@ -29,7 +29,7 @@
#ifndef QBS_JOBS_H
#define QBS_JOBS_H
-#include "../buildgraph/forward_decls.h"
+#include "../language/forward_decls.h"
#include "../tools/error.h"
#include "../tools/qbs_export.h"
@@ -114,8 +114,8 @@ signals:
private:
BuildJob(const Internal::Logger &logger, QObject *parent);
- void build(const QList<Internal::BuildProductPtr> &products, const BuildOptions &options,
- const QProcessEnvironment &env);
+ void build(const QList<qbs::Internal::ResolvedProductPtr> &products,
+ const BuildOptions &options, const QProcessEnvironment &env);
};
@@ -127,7 +127,7 @@ class QBS_EXPORT CleanJob : public AbstractJob
private:
CleanJob(const Internal::Logger &logger, QObject *parent);
- void clean(const QList<Internal::BuildProductPtr> &products, const CleanOptions &options);
+ void clean(const QList<Internal::ResolvedProductPtr> &products, const CleanOptions &options);
};
class QBS_EXPORT InstallJob : public AbstractJob
@@ -137,7 +137,8 @@ class QBS_EXPORT InstallJob : public AbstractJob
private:
InstallJob(const Internal::Logger &logger, QObject *parent);
- void install(const QList<Internal::BuildProductPtr> &products, const InstallOptions &options);
+ void install(const QList<Internal::ResolvedProductPtr> &products,
+ const InstallOptions &options);
};
} // namespace qbs
diff --git a/src/lib/api/project.cpp b/src/lib/api/project.cpp
index eeca9aa78..dc5e55819 100644
--- a/src/lib/api/project.cpp
+++ b/src/lib/api/project.cpp
@@ -34,9 +34,7 @@
#include "propertymap_p.h"
#include "runenvironment.h"
#include <buildgraph/artifact.h>
-#include <buildgraph/buildproduct.h>
-#include <buildgraph/buildproject.h>
-#include <buildgraph/rulesevaluationcontext.h>
+#include <buildgraph/productbuilddata.h>
#include <buildgraph/timestampsupdater.h>
#include <language/language.h>
#include <logging/logger.h>
@@ -92,24 +90,25 @@ static void loadPlugins(const QStringList &_pluginPaths, const Logger &logger)
class ProjectPrivate : public QSharedData
{
public:
- ProjectPrivate(const BuildProjectPtr &internalProject, const Logger &logger)
+ ProjectPrivate(const ResolvedProjectPtr &internalProject, const Logger &logger)
: internalProject(internalProject), logger(logger), m_projectDataRetrieved(false)
{
}
ProjectData projectData();
- BuildJob *buildProducts(const QList<BuildProductPtr> &products, const BuildOptions &options,
+ BuildJob *buildProducts(const QList<ResolvedProductPtr> &products, const BuildOptions &options,
bool needsDepencencyResolving, const QProcessEnvironment &env,
QObject *jobOwner);
- CleanJob *cleanProducts(const QList<BuildProductPtr> &products, const CleanOptions &options,
+ CleanJob *cleanProducts(const QList<ResolvedProductPtr> &products, const CleanOptions &options,
QObject *jobOwner);
- InstallJob *installProducts(const QList<BuildProductPtr> &products,
+ InstallJob *installProducts(const QList<ResolvedProductPtr> &products,
const InstallOptions &options, bool needsDepencencyResolving,
QObject *jobOwner);
- QList<BuildProductPtr> internalProducts(const QList<ProductData> &products) const;
- BuildProductPtr internalProduct(const ProductData &product) const;
+ QList<ResolvedProductPtr> internalProducts(const QList<ProductData> &products) const;
+ QList<ResolvedProductPtr> allEnabledInternalProducts() const;
+ ResolvedProductPtr internalProduct(const ProductData &product) const;
- const BuildProjectPtr internalProject;
+ const ResolvedProjectPtr internalProject;
Logger logger;
private:
@@ -126,22 +125,22 @@ ProjectData ProjectPrivate::projectData()
return m_projectData;
}
-static void addDependencies(QList<BuildProductPtr> &products)
+static void addDependencies(QList<ResolvedProductPtr> &products)
{
for (int i = 0; i < products.count(); ++i) {
- const BuildProductConstPtr &product = products.at(i);
- foreach (const BuildProductPtr &dependency, product->dependencies) {
+ const ResolvedProductPtr &product = products.at(i);
+ foreach (const ResolvedProductPtr &dependency, product->dependencies) {
if (!products.contains(dependency))
products << dependency;
}
}
}
-BuildJob *ProjectPrivate::buildProducts(const QList<BuildProductPtr> &products,
+BuildJob *ProjectPrivate::buildProducts(const QList<ResolvedProductPtr> &products,
const BuildOptions &options, bool needsDepencencyResolving,
const QProcessEnvironment &env, QObject *jobOwner)
{
- QList<BuildProductPtr> productsToBuild = products;
+ QList<ResolvedProductPtr> productsToBuild = products;
if (needsDepencencyResolving)
addDependencies(productsToBuild);
@@ -150,7 +149,7 @@ BuildJob *ProjectPrivate::buildProducts(const QList<BuildProductPtr> &products,
return job;
}
-CleanJob *ProjectPrivate::cleanProducts(const QList<BuildProductPtr> &products,
+CleanJob *ProjectPrivate::cleanProducts(const QList<ResolvedProductPtr> &products,
const CleanOptions &options, QObject *jobOwner)
{
CleanJob * const job = new CleanJob(logger, jobOwner);
@@ -158,10 +157,10 @@ CleanJob *ProjectPrivate::cleanProducts(const QList<BuildProductPtr> &products,
return job;
}
-InstallJob *ProjectPrivate::installProducts(const QList<BuildProductPtr> &products,
+InstallJob *ProjectPrivate::installProducts(const QList<ResolvedProductPtr> &products,
const InstallOptions &options, bool needsDepencencyResolving, QObject *jobOwner)
{
- QList<BuildProductPtr> productsToInstall = products;
+ QList<ResolvedProductPtr> productsToInstall = products;
if (needsDepencencyResolving)
addDependencies(productsToInstall);
InstallJob * const job = new InstallJob(logger, jobOwner);
@@ -169,9 +168,9 @@ InstallJob *ProjectPrivate::installProducts(const QList<BuildProductPtr> &produc
return job;
}
-QList<BuildProductPtr> ProjectPrivate::internalProducts(const QList<ProductData> &products) const
+QList<ResolvedProductPtr> ProjectPrivate::internalProducts(const QList<ProductData> &products) const
{
- QList<BuildProductPtr> internalProducts;
+ QList<ResolvedProductPtr> internalProducts;
foreach (const ProductData &product, products) {
if (product.isEnabled())
internalProducts << internalProduct(product);
@@ -179,21 +178,30 @@ QList<BuildProductPtr> ProjectPrivate::internalProducts(const QList<ProductData>
return internalProducts;
}
-BuildProductPtr ProjectPrivate::internalProduct(const ProductData &product) const
+QList<ResolvedProductPtr> ProjectPrivate::allEnabledInternalProducts() const
{
- foreach (const BuildProductPtr &buildProduct, internalProject->buildProducts()) {
- if (product.name() == buildProduct->rProduct->name)
- return buildProduct;
+ QList<ResolvedProductPtr> products;
+ foreach (const ResolvedProductPtr &p, internalProject->products) {
+ if (p->enabled)
+ products << p;
+ }
+ return products;
+}
+
+ResolvedProductPtr ProjectPrivate::internalProduct(const ProductData &product) const
+{
+ foreach (const ResolvedProductPtr &resolvedProduct, internalProject->products) {
+ if (product.name() == resolvedProduct->name)
+ return resolvedProduct;
}
qFatal("No build product '%s'", qPrintable(product.name()));
- return BuildProductPtr();
+ return ResolvedProductPtr();
}
void ProjectPrivate::retrieveProjectData()
{
- m_projectData.m_location = internalProject->resolvedProject()->location;
- foreach (const ResolvedProductConstPtr &resolvedProduct,
- internalProject->resolvedProject()->products) {
+ m_projectData.m_location = internalProject->location;
+ foreach (const ResolvedProductConstPtr &resolvedProduct, internalProject->products) {
ProductData product;
product.m_name = resolvedProduct->name;
product.m_location = resolvedProduct->location;
@@ -232,7 +240,7 @@ using namespace Internal;
* \brief The \c Project class provides services related to a qbs project.
*/
-Project::Project(const BuildProjectPtr &internalProject, const Logger &logger)
+Project::Project(const ResolvedProjectPtr &internalProject, const Logger &logger)
: d(new ProjectPrivate(internalProject, logger))
{
}
@@ -357,11 +365,11 @@ QString Project::targetExecutable(const ProductData &product, const QString &_in
{
if (!product.isEnabled())
return QString();
- const BuildProductConstPtr buildProduct = d->internalProduct(product);
- if (!isExecutable(buildProduct->rProduct->properties, buildProduct->rProduct->fileTags))
+ const ResolvedProductConstPtr internalProduct = d->internalProduct(product);
+ if (!isExecutable(internalProduct->properties, internalProduct->fileTags))
return QString();
- foreach (const Artifact * const artifact, buildProduct->targetArtifacts) {
+ foreach (const Artifact * const artifact, internalProduct->buildData->targetArtifacts) {
if (isExecutable(artifact->properties, artifact->fileTags)) {
if (!artifact->properties->qbsPropertyValue(QLatin1String("install")).toBool())
return artifact->filePath();
@@ -376,12 +384,13 @@ QString Project::targetExecutable(const ProductData &product, const QString &_in
}
if (installRoot.isEmpty()) {
- installRoot = artifact->product->project->resolvedProject()->buildDirectory
+ installRoot = d->internalProject->buildDirectory
+ QLatin1Char('/') + InstallOptions::defaultInstallRoot();
}
QString installDir = artifact->properties
->qbsPropertyValue(QLatin1String("installDir")).toString();
- return QDir::cleanPath(installRoot + QLatin1Char('/') + installDir + QLatin1Char('/') + fileName);
+ return QDir::cleanPath(installRoot + QLatin1Char('/') + installDir + QLatin1Char('/')
+ + fileName);
}
}
return QString();
@@ -391,7 +400,7 @@ RunEnvironment Project::getRunEnvironment(const ProductData &product,
const QProcessEnvironment &environment, Settings *settings) const
{
QBS_CHECK(product.isEnabled());
- const ResolvedProductPtr resolvedProduct = d->internalProduct(product)->rProduct;
+ const ResolvedProductPtr resolvedProduct = d->internalProduct(product);
return RunEnvironment(resolvedProduct, environment, settings, d->logger);
}
@@ -402,8 +411,7 @@ RunEnvironment Project::getRunEnvironment(const ProductData &product,
BuildJob *Project::buildAllProducts(const BuildOptions &options, const QProcessEnvironment &env,
QObject *jobOwner) const
{
- return d->buildProducts(d->internalProject->buildProducts().toList(), options, false, env,
- jobOwner);
+ return d->buildProducts(d->allEnabledInternalProducts(), options, false, env, jobOwner);
}
/*!
@@ -436,7 +444,7 @@ BuildJob *Project::buildOneProduct(const ProductData &product, const BuildOption
*/
CleanJob *Project::cleanAllProducts(const CleanOptions &options, QObject *jobOwner) const
{
- return d->cleanProducts(d->internalProject->buildProducts().toList(), options, jobOwner);
+ return d->cleanProducts(d->allEnabledInternalProducts(), options, jobOwner);
}
/*!
@@ -465,8 +473,7 @@ CleanJob *Project::cleanOneProduct(const ProductData &product, const CleanOption
*/
InstallJob *Project::installAllProducts(const InstallOptions &options, QObject *jobOwner) const
{
- return d->installProducts(d->internalProject->buildProducts().toList(), options, false,
- jobOwner);
+ return d->installProducts(d->allEnabledInternalProducts(), options, false, jobOwner);
}
/*!
@@ -495,7 +502,7 @@ InstallJob *Project::installOneProduct(const ProductData &product, const Install
*/
void Project::updateTimestamps(const QList<ProductData> &products)
{
- TimestampsUpdater().updateTimestamps(d->internalProducts(products));
+ TimestampsUpdater().updateTimestamps(d->internalProducts(products), d->logger);
}
} // namespace qbs
diff --git a/src/lib/api/project.h b/src/lib/api/project.h
index c6072f312..ec9ac3a46 100644
--- a/src/lib/api/project.h
+++ b/src/lib/api/project.h
@@ -29,7 +29,7 @@
#ifndef QBS_PROJECT_H
#define QBS_PROJECT_H
-#include "../buildgraph/forward_decls.h"
+#include "../language/forward_decls.h"
#include "../tools/qbs_export.h"
#include <QExplicitlySharedDataPointer>
@@ -56,7 +56,6 @@ class SetupProjectJob;
class SetupProjectParameters;
namespace Internal {
-class BuildProject;
class Logger;
class ProjectPrivate;
} // namespace Internal;
@@ -104,7 +103,7 @@ public:
private:
Project();
- Project(const Internal::BuildProjectPtr &internalProject, const Internal::Logger &logger);
+ Project(const Internal::ResolvedProjectPtr &internalProject, const Internal::Logger &logger);
QExplicitlySharedDataPointer<Internal::ProjectPrivate> d;
};
diff --git a/src/lib/buildgraph/artifact.cpp b/src/lib/buildgraph/artifact.cpp
index c14d3eecd..e2de97d75 100644
--- a/src/lib/buildgraph/artifact.cpp
+++ b/src/lib/buildgraph/artifact.cpp
@@ -31,6 +31,7 @@
#include "transformer.h"
#include "buildgraph.h"
+#include <language/language.h>
#include <language/propertymapinternal.h>
#include <logging/logger.h>
#include <tools/fileinfo.h>
@@ -56,22 +57,30 @@ QT_END_NAMESPACE
namespace qbs {
namespace Internal {
-Artifact::Artifact(BuildProject *p)
- : project(p)
- , product(0)
- , transformer(0)
- , artifactType(Unknown)
- , buildState(Untouched)
- , inputsScanned(false)
- , timestampRetrieved(false)
- , alwaysUpdated(true)
+Artifact::Artifact()
{
+ initialize();
+}
+
+Artifact::Artifact(const ResolvedProjectPtr &project)
+ : project(project)
+{
+ initialize();
}
Artifact::~Artifact()
{
}
+void Artifact::initialize()
+{
+ artifactType = Unknown;
+ buildState = Untouched;
+ inputsScanned = false;
+ timestampRetrieved = false;
+ alwaysUpdated = true;
+}
+
void Artifact::setFilePath(const QString &filePath)
{
m_filePath = filePath;
diff --git a/src/lib/buildgraph/artifact.h b/src/lib/buildgraph/artifact.h
index b6e88d93a..03e6a9071 100644
--- a/src/lib/buildgraph/artifact.h
+++ b/src/lib/buildgraph/artifact.h
@@ -36,14 +36,12 @@
#include <language/forward_decls.h>
#include <tools/filetime.h>
#include <tools/persistentobject.h>
+#include <tools/weakpointer.h>
#include <QString>
namespace qbs {
namespace Internal {
-
-class BuildProduct;
-class BuildProject;
class Logger;
/**
@@ -57,15 +55,20 @@ class Logger;
class Artifact : public PersistentObject
{
public:
- Artifact(BuildProject *p = 0);
+ // We could save one constructor here by using a default argument, but then we'd have to
+ // include language.h in every file that includes artifact.h due to Qt 4's weird
+ // QSharedPointer implementation.
+ Artifact();
+ explicit Artifact(const ResolvedProjectPtr &project);
+
~Artifact();
ArtifactList parents;
ArtifactList children;
ArtifactList fileDependencies;
FileTags fileTags;
- BuildProject *project;
- BuildProduct *product; // Note: file dependency artifacts don't belong to a product.
+ WeakPointer<ResolvedProject> project;
+ WeakPointer<ResolvedProduct> product; // Note: file dependency artifacts don't belong to a product.
TransformerPtr transformer;
PropertyMapPtr properties;
@@ -93,6 +96,7 @@ public:
bool timestampRetrieved : 1; // Do not serialize. Will be refreshed for every build.
bool alwaysUpdated : 1;
+ void initialize();
void setFilePath(const QString &filePath);
const QString &filePath() const { return m_filePath; }
QString dirPath() const { return m_dirPath.toString(); }
diff --git a/src/lib/buildgraph/artifactcleaner.cpp b/src/lib/buildgraph/artifactcleaner.cpp
index 1fe5013b5..12228144a 100644
--- a/src/lib/buildgraph/artifactcleaner.cpp
+++ b/src/lib/buildgraph/artifactcleaner.cpp
@@ -30,14 +30,10 @@
#include "artifact.h"
#include "artifactvisitor.h"
-#include "buildproduct.h"
-#include "buildproject.h"
-#include "rulesevaluationcontext.h"
-#include "transformer.h"
+#include "projectbuilddata.h"
#include <language/language.h>
#include <logging/translator.h>
-#include <tools/buildoptions.h>
#include <tools/cleanoptions.h>
#include <tools/error.h>
#include <tools/fileinfo.h>
@@ -65,7 +61,7 @@ static void invalidateArtifactTimestamp(Artifact *artifact)
{
if (artifact->timestamp.isValid()) {
artifact->timestamp.clear();
- artifact->project->markDirty();
+ artifact->project->buildData->isDirty = true;
}
}
@@ -97,7 +93,7 @@ public:
{
}
- void visitProduct(const BuildProductConstPtr &product)
+ void visitProduct(const ResolvedProductConstPtr &product)
{
m_product = product;
ArtifactVisitor::visitProduct(product);
@@ -127,7 +123,7 @@ private:
const CleanOptions m_options;
Logger m_logger;
bool m_hasError;
- BuildProductConstPtr m_product;
+ ResolvedProductConstPtr m_product;
QSet<QString> m_directories;
};
@@ -136,19 +132,20 @@ ArtifactCleaner::ArtifactCleaner(const Logger &logger, ProgressObserver *observe
{
}
-void ArtifactCleaner::cleanup(const QList<BuildProductPtr> &products, const CleanOptions &options)
+void ArtifactCleaner::cleanup(const QList<ResolvedProductPtr> &products,
+ const CleanOptions &options)
{
m_hasError = false;
QString configString;
if (!products.isEmpty()) {
configString = Tr::tr(" for configuration %1")
- .arg(products.first()->project->resolvedProject()->id());
+ .arg(products.first()->project->id());
}
m_observer->initialize(Tr::tr("Cleaning up%1").arg(configString), products.count() + 1);
QSet<QString> directories;
- foreach (const BuildProductConstPtr &product, products) {
+ foreach (const ResolvedProductPtr &product, products) {
CleanupVisitor visitor(options, m_logger);
visitor.visitProduct(product);
directories.unite(visitor.directories());
diff --git a/src/lib/buildgraph/artifactcleaner.h b/src/lib/buildgraph/artifactcleaner.h
index e7390b0e9..e3d06b4f5 100644
--- a/src/lib/buildgraph/artifactcleaner.h
+++ b/src/lib/buildgraph/artifactcleaner.h
@@ -31,8 +31,7 @@
#include <QList>
-#include "forward_decls.h"
-
+#include <language/forward_decls.h>
#include <logging/logger.h>
namespace qbs {
@@ -45,7 +44,7 @@ class ArtifactCleaner
{
public:
ArtifactCleaner(const Logger &logger, ProgressObserver *observer);
- void cleanup(const QList<BuildProductPtr> &products, const CleanOptions &options);
+ void cleanup(const QList<ResolvedProductPtr> &products, const CleanOptions &options);
private:
void removeEmptyDirectories(const QString &rootDir, const CleanOptions &options,
diff --git a/src/lib/buildgraph/artifactvisitor.cpp b/src/lib/buildgraph/artifactvisitor.cpp
index cbd2726ee..7746ded85 100644
--- a/src/lib/buildgraph/artifactvisitor.cpp
+++ b/src/lib/buildgraph/artifactvisitor.cpp
@@ -29,9 +29,7 @@
#include "artifactvisitor.h"
#include "artifact.h"
-#include "buildproduct.h"
-#include "buildproject.h"
-#include "rulesevaluationcontext.h"
+#include "productbuilddata.h"
#include <language/language.h>
#include <tools/qbsassert.h>
@@ -56,15 +54,17 @@ void ArtifactVisitor::visitArtifact(Artifact *artifact)
visitArtifact(child);
}
-void ArtifactVisitor::visitProduct(const BuildProductConstPtr &product)
+void ArtifactVisitor::visitProduct(const ResolvedProductConstPtr &product)
{
- foreach (Artifact * const artifact, product->targetArtifacts)
+ if (!product->buildData)
+ return;
+ foreach (Artifact * const artifact, product->buildData->targetArtifacts)
visitArtifact(artifact);
}
-void ArtifactVisitor::visitProject(const BuildProjectConstPtr &project)
+void ArtifactVisitor::visitProject(const ResolvedProjectConstPtr &project)
{
- foreach (const BuildProductConstPtr &product, project->buildProducts())
+ foreach (const ResolvedProductConstPtr &product, project->products)
visitProduct(product);
}
diff --git a/src/lib/buildgraph/artifactvisitor.h b/src/lib/buildgraph/artifactvisitor.h
index b4fa16e35..6ff6a4f11 100644
--- a/src/lib/buildgraph/artifactvisitor.h
+++ b/src/lib/buildgraph/artifactvisitor.h
@@ -31,6 +31,8 @@
#include "forward_decls.h"
+#include <language/forward_decls.h>
+
#include <QList>
#include <QSet>
@@ -43,8 +45,8 @@ public:
ArtifactVisitor(int artifactType);
virtual void visitArtifact(Artifact *artifact);
- virtual void visitProduct(const BuildProductConstPtr &product);
- virtual void visitProject(const BuildProjectConstPtr &project);
+ virtual void visitProduct(const ResolvedProductConstPtr &product);
+ virtual void visitProject(const ResolvedProjectConstPtr &project);
private:
virtual void doVisit(Artifact *artifact) = 0;
diff --git a/src/lib/buildgraph/automoc.cpp b/src/lib/buildgraph/automoc.cpp
index dab1f9e5f..bf97caf50 100644
--- a/src/lib/buildgraph/automoc.cpp
+++ b/src/lib/buildgraph/automoc.cpp
@@ -28,11 +28,10 @@
****************************************************************************/
#include "automoc.h"
-#include "buildproduct.h"
-#include "buildproject.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
#include "buildgraph.h"
#include "rulesapplicator.h"
-#include "rulesevaluationcontext.h"
#include "scanresultcache.h"
#include <buildgraph/artifact.h>
#include <buildgraph/transformer.h>
@@ -80,7 +79,7 @@ void AutoMoc::setScanResultCache(ScanResultCache *scanResultCache)
m_scanResultCache = scanResultCache;
}
-void AutoMoc::apply(const BuildProductPtr &product)
+void AutoMoc::apply(const ResolvedProductPtr &product)
{
if (scanners().isEmpty())
throw Error("C++ scanner cannot be loaded.");
@@ -90,8 +89,8 @@ void AutoMoc::apply(const BuildProductPtr &product)
QList<QPair<Artifact *, FileType> > artifactsToMoc;
QSet<QString> includedMocCppFiles;
const FileTime currentTime = FileTime::currentTime();
- ArtifactList::const_iterator it = product->artifacts.begin();
- for (; it != product->artifacts.end(); ++it) {
+ ArtifactList::const_iterator it = product->buildData->artifacts.begin();
+ for (; it != product->buildData->artifacts.end(); ++it) {
Artifact *artifact = *it;
if (!pchFile || !pluginMetaDataFile) {
foreach (const FileTag &fileTag, artifact->fileTags) {
@@ -164,8 +163,8 @@ void AutoMoc::apply(const BuildProductPtr &product)
if (!artifactsPerFileTag.isEmpty()) {
emit reportCommandDescription(QLatin1String("automoc"),
Tr::tr("Applying moc rules for '%1'.")
- .arg(product->rProduct->name));
- RulesApplicator(product.data(), artifactsPerFileTag, m_logger).applyAllRules();
+ .arg(product->name));
+ RulesApplicator(product, artifactsPerFileTag, m_logger).applyAllRules();
}
if (pluginHeaderFile && pluginMetaDataFile) {
// Make every artifact that is dependent of the header file also
@@ -174,7 +173,7 @@ void AutoMoc::apply(const BuildProductPtr &product)
loggedConnect(outputOfHeader, pluginMetaDataFile, m_logger);
}
- product->project->updateNodesThatMustGetNewTransformer();
+ product->project->buildData->updateNodesThatMustGetNewTransformer(m_logger);
}
QString AutoMoc::generateMocFileName(Artifact *artifact, FileType fileType)
@@ -345,7 +344,7 @@ void AutoMoc::unmoc(Artifact *artifact, const FileTag &mocFileTag)
m_logger.qbsTrace() << "[AUTOMOC] removing moc obj artifact "
<< relativeArtifactFileName(mocObjArtifact);
}
- artifact->project->removeArtifact(mocObjArtifact);
+ artifact->project->buildData->removeArtifact(mocObjArtifact, m_logger);
}
}
@@ -353,7 +352,7 @@ void AutoMoc::unmoc(Artifact *artifact, const FileTag &mocFileTag)
m_logger.qbsTrace() << "[AUTOMOC] removing generated artifact "
<< relativeArtifactFileName(generatedMocArtifact);
}
- artifact->project->removeArtifact(generatedMocArtifact);
+ artifact->project->buildData->removeArtifact(generatedMocArtifact, m_logger);
delete generatedMocArtifact;
}
diff --git a/src/lib/buildgraph/automoc.h b/src/lib/buildgraph/automoc.h
index 6d6b7c510..a24e7320f 100644
--- a/src/lib/buildgraph/automoc.h
+++ b/src/lib/buildgraph/automoc.h
@@ -32,6 +32,7 @@
#include "forward_decls.h"
+#include <language/forward_decls.h>
#include <logging/logger.h>
#include <QObject>
@@ -59,7 +60,7 @@ public:
AutoMoc(const Logger &logger, QObject *parent = 0);
void setScanResultCache(ScanResultCache *scanResultCache);
- void apply(const BuildProductPtr &product);
+ void apply(const ResolvedProductPtr &product);
signals:
void reportCommandDescription(const QString &highlight, const QString &message);
diff --git a/src/lib/buildgraph/buildgraph.cpp b/src/lib/buildgraph/buildgraph.cpp
index 81e01cc51..1144f7160 100644
--- a/src/lib/buildgraph/buildgraph.cpp
+++ b/src/lib/buildgraph/buildgraph.cpp
@@ -29,17 +29,27 @@
#include "buildgraph.h"
#include "artifact.h"
-#include "buildproject.h"
+#include "projectbuilddata.h"
+#include "productbuilddata.h"
+#include "cycledetector.h"
#include "rulesevaluationcontext.h"
+#include "transformer.h"
#include <jsextensions/moduleproperties.h>
+#include <language/loader.h>
#include <language/language.h>
#include <language/scriptengine.h>
#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/error.h>
#include <tools/fileinfo.h>
+#include <tools/propertyfinder.h>
#include <tools/qbsassert.h>
+#include <tools/setupprojectparameters.h>
+#include <QDir>
#include <QFile>
+#include <QFileInfo>
namespace qbs {
namespace Internal {
@@ -238,7 +248,7 @@ void connect(Artifact *p, Artifact *c)
QBS_CHECK(p != c);
p->children.insert(c);
c->parents.insert(p);
- p->project->markDirty();
+ p->project->buildData->isDirty = true;
}
void loggedConnect(Artifact *u, Artifact *v, const Logger &logger)
@@ -310,7 +320,7 @@ void removeGeneratedArtifactFromDisk(Artifact *artifact, const Logger &logger)
QString relativeArtifactFileName(const Artifact *n)
{
- const QString &buildDir = n->project->resolvedProject()->buildDirectory;
+ const QString &buildDir = n->project->buildDirectory;
QString str = n->filePath();
if (str.startsWith(buildDir))
str.remove(0, buildDir.count());
@@ -319,5 +329,447 @@ QString relativeArtifactFileName(const Artifact *n)
return str;
}
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const QString &dirPath,
+ const QString &fileName)
+{
+ QList<Artifact *> artifacts
+ = product->project->buildData->lookupArtifacts(dirPath, fileName);
+ for (QList<Artifact *>::const_iterator it = artifacts.constBegin();
+ it != artifacts.constEnd(); ++it) {
+ if ((*it)->product == product)
+ return *it;
+ }
+ return 0;
+}
+
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const QString &filePath)
+{
+ QString dirPath, fileName;
+ FileInfo::splitIntoDirectoryAndFileName(filePath, &dirPath, &fileName);
+ return lookupArtifact(product, dirPath, fileName);
+}
+
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const Artifact *artifact)
+{
+ return lookupArtifact(product, artifact->dirPath(), artifact->fileName());
+}
+
+Artifact *createArtifact(const ResolvedProductPtr &product,
+ const SourceArtifactConstPtr &sourceArtifact, const Logger &logger)
+{
+ Artifact *artifact = new Artifact(product->project);
+ artifact->artifactType = Artifact::SourceFile;
+ artifact->setFilePath(sourceArtifact->absoluteFilePath);
+ artifact->fileTags = sourceArtifact->fileTags;
+ artifact->properties = sourceArtifact->properties;
+ insertArtifact(product, artifact, logger);
+ return artifact;
+}
+
+void insertArtifact(const ResolvedProductPtr &product, Artifact *artifact, const Logger &logger)
+{
+ QBS_CHECK(!artifact->product);
+ QBS_CHECK(!artifact->filePath().isEmpty());
+ QBS_CHECK(!product->buildData->artifacts.contains(artifact));
+#ifdef QT_DEBUG
+ foreach (const ResolvedProductConstPtr &otherProduct, product->project->products) {
+ if (lookupArtifact(otherProduct, artifact->filePath())) {
+ if (artifact->artifactType == Artifact::Generated) {
+ QString pl;
+ pl.append(QString(" - %1 \n").arg(product->name));
+ foreach (const ResolvedProductConstPtr &p, product->project->products) {
+ if (lookupArtifact(p, artifact->filePath()))
+ pl.append(QString(" - %1 \n").arg(p->name));
+ }
+ throw Error(QString ("BUG: already inserted in this project: %1\n%2")
+ .arg(artifact->filePath()).arg(pl));
+ }
+ }
+ }
+#endif
+ product->buildData->artifacts.insert(artifact);
+ artifact->product = product;
+ product->project->buildData->insertIntoArtifactLookupTable(artifact);
+ product->project->buildData->isDirty = true;
+
+ if (logger.traceEnabled()) {
+ logger.qbsTrace() << QString::fromLocal8Bit("[BG] insert artifact '%1'")
+ .arg(artifact->filePath());
+ }
+}
+
+static void dumpProductBuildDataInternal(const ResolvedProductConstPtr &product, Artifact *artifact,
+ QByteArray indent)
+{
+ Artifact *artifactInProduct = lookupArtifact(product, artifact->filePath());
+ if (artifactInProduct && artifactInProduct != artifact) {
+ qFatal("\ntree corrupted. %p ('%s') resolves to %p ('%s')\n",
+ artifact, qPrintable(artifact->filePath()), lookupArtifact(product, artifact->filePath()),
+ qPrintable(lookupArtifact(product, artifact->filePath())->filePath()));
+
+ }
+ printf("%s", indent.constData());
+ printf("Artifact (%p) ", artifact);
+ printf("%s%s %s [%s]",
+ qPrintable(QString(toString(artifact->buildState).at(0))),
+ artifactInProduct ? "" : " SBS", // SBS == side-by-side artifact from other product
+ qPrintable(artifact->filePath()),
+ qPrintable(artifact->fileTags.toStringList().join(QLatin1String(", "))));
+ printf("\n");
+ indent.append(" ");
+ foreach (Artifact *child, artifact->children) {
+ dumpProductBuildDataInternal(product, child, indent);
+ }
+}
+
+void dumpProductBuildData(const ResolvedProductConstPtr &product)
+{
+ foreach (Artifact *artifact, product->buildData->artifacts)
+ if (artifact->parents.isEmpty())
+ dumpProductBuildDataInternal(product, artifact, QByteArray());
+}
+
+
+BuildGraphLoader::BuildGraphLoader(const Logger &logger) : m_logger(logger)
+{
+}
+
+static bool isConfigCompatible(const QVariantMap &userCfg, const QVariantMap &projectCfg)
+{
+ QVariantMap::const_iterator it = userCfg.begin();
+ for (; it != userCfg.end(); ++it) {
+ if (it.value().type() == QVariant::Map) {
+ if (!isConfigCompatible(it.value().toMap(), projectCfg.value(it.key()).toMap()))
+ return false;
+ } else {
+ QVariant value = projectCfg.value(it.key());
+ if (!value.isNull() && value != it.value()) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+BuildGraphLoader::LoadResult BuildGraphLoader::load(const SetupProjectParameters &parameters,
+ const RulesEvaluationContextPtr &evalContext)
+{
+ m_result = LoadResult();
+ m_evalContext = evalContext;
+
+ const QString projectId = ResolvedProject::deriveId(parameters.buildConfiguration);
+ const QString buildDir = ResolvedProject::deriveBuildDirectory(parameters.buildRoot, projectId);
+ const QString buildGraphFilePath
+ = ProjectBuildData::deriveBuildGraphFilePath(buildDir, projectId);
+
+ PersistentPool pool(m_logger);
+ m_logger.qbsDebug() << "[BG] trying to load: " << buildGraphFilePath;
+ if (!pool.load(buildGraphFilePath))
+ return m_result;
+ if (!isConfigCompatible(parameters.buildConfiguration, pool.headData().projectConfig)) {
+ m_logger.qbsDebug() << "[BG] Cannot use stored build graph: "
+ "Incompatible project configuration.";
+ return m_result;
+ }
+
+ const ResolvedProjectPtr project = ResolvedProject::create();
+ TimedActivityLogger loadLogger(m_logger, QLatin1String("Loading build graph"),
+ QLatin1String("[BG] "));
+ project->load(pool);
+ project->buildData->evaluationContext = evalContext;
+ foreach (Artifact * const a, project->buildData->dependencyArtifacts)
+ a->project = project;
+ foreach (const ResolvedProductPtr &product, project->products) {
+ if (!product->buildData)
+ continue;
+ foreach (Artifact * const a, product->buildData->artifacts) {
+ a->project = project;
+ project->buildData->insertIntoArtifactLookupTable(a);
+ }
+ }
+
+ if (QFileInfo(project->location.fileName) != QFileInfo(parameters.projectFilePath)) {
+ QString errorMessage = Tr::tr("Stored build graph is for project file '%1', but "
+ "input file is '%2'. ")
+ .arg(QDir::toNativeSeparators(project->location.fileName),
+ QDir::toNativeSeparators(parameters.projectFilePath));
+ if (!parameters.ignoreDifferentProjectFilePath) {
+ errorMessage += Tr::tr("Aborting.");
+ throw Error(errorMessage);
+ }
+
+ // Okay, let's assume it's the same project anyway (the source dir might have moved).
+ errorMessage += Tr::tr("Ignoring.");
+ m_logger.qbsWarning() << errorMessage;
+ }
+ foreach (const ResolvedProductPtr &p, project->products)
+ p->project = project;
+ project->location = CodeLocation(parameters.projectFilePath, 1, 1);
+ project->setBuildConfiguration(pool.headData().projectConfig);
+ project->buildDirectory = buildDir;
+ m_result.loadedProject = project;
+ loadLogger.finishActivity();
+ trackProjectChanges(parameters, buildGraphFilePath, project);
+ return m_result;
+}
+
+void BuildGraphLoader::trackProjectChanges(const SetupProjectParameters &parameters,
+ const QString &buildGraphFilePath, const ResolvedProjectPtr &restoredProject)
+{
+ const FileInfo bgfi(buildGraphFilePath);
+ const bool projectFileChanged
+ = bgfi.lastModified() < FileInfo(parameters.projectFilePath).lastModified();
+
+ bool referencedProductRemoved = false;
+ QList<ResolvedProductPtr> changedProducts;
+ foreach (const ResolvedProductPtr &product, restoredProject->products) {
+ const FileInfo pfi(product->location.fileName);
+ if (!pfi.exists()) {
+ referencedProductRemoved = true;
+ } else if (bgfi.lastModified() < pfi.lastModified()) {
+ changedProducts += product;
+ } else {
+ foreach (const GroupPtr &group, product->groups) {
+ if (!group->wildcards)
+ continue;
+ const QSet<QString> files
+ = group->wildcards->expandPatterns(group, product->sourceDirectory);
+ QSet<QString> wcFiles;
+ foreach (const SourceArtifactConstPtr &sourceArtifact, group->wildcards->files)
+ wcFiles += sourceArtifact->absoluteFilePath;
+ if (files == wcFiles)
+ continue;
+ changedProducts += product;
+ break;
+ }
+ }
+ }
+
+ if (!projectFileChanged && !referencedProductRemoved && changedProducts.isEmpty())
+ return;
+
+ Loader ldr(m_evalContext->engine(), m_logger);
+ ldr.setSearchPaths(parameters.searchPaths);
+ m_result.newlyResolvedProject = ldr.loadProject(parameters);
+
+ QMap<QString, ResolvedProductPtr> freshProductsByName;
+ foreach (const ResolvedProductPtr &cp, m_result.newlyResolvedProject->products)
+ freshProductsByName.insert(cp->name, cp);
+
+ QSet<TransformerPtr> seenTransformers;
+ foreach (const ResolvedProductPtr &product, restoredProject->products) {
+ foreach (Artifact *artifact, product->buildData->artifacts) {
+ if (!artifact->transformer || seenTransformers.contains(artifact->transformer))
+ continue;
+ seenTransformers.insert(artifact->transformer);
+ ResolvedProductPtr freshProduct = freshProductsByName.value(product->name);
+ if (freshProduct && checkForPropertyChanges(artifact->transformer, freshProduct)) {
+ m_result.discardLoadedProject = true;
+ return;
+ }
+ }
+ }
+
+ foreach (const ResolvedProductPtr &product, changedProducts) {
+ ResolvedProductPtr freshProduct = freshProductsByName.value(product->name);
+ if (!freshProduct)
+ continue;
+ onProductChanged(product, freshProduct);
+ if (m_result.discardLoadedProject)
+ return;
+ }
+
+ QSet<QString> oldProductNames, newProductNames;
+ foreach (const ResolvedProductConstPtr &product, restoredProject->products)
+ oldProductNames += product->name;
+ foreach (const ResolvedProductConstPtr &product, m_result.newlyResolvedProject->products)
+ newProductNames += product->name;
+
+ const QSet<QString> removedProductsNames = oldProductNames - newProductNames;
+ if (!removedProductsNames.isEmpty()) {
+ foreach (const ResolvedProductPtr &product, restoredProject->products) {
+ if (removedProductsNames.contains(product->name))
+ onProductRemoved(product);
+ }
+ }
+
+ const QSet<QString> addedProductNames = newProductNames - oldProductNames;
+ QList<ResolvedProductPtr> addedProducts;
+ foreach (const QString &productName, addedProductNames) {
+ const ResolvedProductPtr &freshProduct = freshProductsByName.value(productName);
+ QBS_ASSERT(freshProduct, continue);
+ addedProducts.append(freshProduct);
+ }
+ if (!addedProducts.isEmpty()) {
+ foreach (const ResolvedProductPtr &product, addedProducts) {
+ product->project = restoredProject;
+ restoredProject->products.append(product);
+ }
+ BuildDataResolver bpr(m_logger);
+ bpr.resolveProductBuildDataForExistingProject(restoredProject, addedProducts);
+ }
+
+ CycleDetector(m_logger).visitProject(restoredProject);
+}
+
+void BuildGraphLoader::onProductRemoved(const ResolvedProductPtr &product)
+{
+ m_logger.qbsDebug() << "[BG] product '" << product->name << "' removed.";
+
+ product->project->buildData->isDirty = true;
+ product->project->products.removeOne(product);
+
+ // delete all removed artifacts physically from the disk
+ foreach (Artifact *artifact, product->buildData->artifacts) {
+ artifact->disconnectAll(m_logger);
+ removeGeneratedArtifactFromDisk(artifact, m_logger);
+ }
+}
+
+void BuildGraphLoader::onProductChanged(const ResolvedProductPtr &product,
+ const ResolvedProductPtr &changedProduct)
+{
+ m_logger.qbsDebug() << "[BG] product '" << product->name << "' changed.";
+
+ ArtifactsPerFileTagMap artifactsPerFileTag;
+ QSet<SourceArtifactConstPtr> addedSourceArtifacts;
+ QList<Artifact *> addedArtifacts, artifactsToRemove;
+ QHash<QString, SourceArtifactConstPtr> oldArtifacts, newArtifacts;
+
+ const QList<SourceArtifactPtr> oldProductAllFiles = product->allEnabledFiles();
+ foreach (const SourceArtifactConstPtr &a, oldProductAllFiles)
+ oldArtifacts.insert(a->absoluteFilePath, a);
+ foreach (const SourceArtifactPtr &a, changedProduct->allEnabledFiles()) {
+ newArtifacts.insert(a->absoluteFilePath, a);
+ if (!oldArtifacts.contains(a->absoluteFilePath)) {
+ // artifact added
+ m_logger.qbsDebug() << "[BG] artifact '" << a->absoluteFilePath
+ << "' added to product " << product->name;
+ addedArtifacts += createArtifact(product, a, m_logger);
+ addedSourceArtifacts += a;
+ }
+ }
+
+ foreach (const SourceArtifactPtr &a, oldProductAllFiles) {
+ const SourceArtifactConstPtr changedArtifact = newArtifacts.value(a->absoluteFilePath);
+ if (!changedArtifact) {
+ // artifact removed
+ m_logger.qbsDebug() << "[BG] artifact '" << a->absoluteFilePath
+ << "' removed from product " << product->name;
+ Artifact *artifact = lookupArtifact(product, a->absoluteFilePath);
+ QBS_CHECK(artifact);
+ removeArtifactAndExclusiveDependents(artifact, &artifactsToRemove);
+ continue;
+ }
+ if (changedArtifact->fileTags != a->fileTags) {
+ // artifact's filetags have changed
+ m_logger.qbsDebug() << "[BG] filetags have changed for artifact '"
+ << a->absoluteFilePath << "' from " << a->fileTags << " to "
+ << changedArtifact->fileTags;
+ Artifact *artifact = lookupArtifact(product, a->absoluteFilePath);
+ QBS_CHECK(artifact);
+
+ // handle added filetags
+ foreach (const FileTag &addedFileTag, changedArtifact->fileTags - a->fileTags)
+ artifactsPerFileTag[addedFileTag] += artifact;
+
+ // handle removed filetags
+ foreach (const FileTag &removedFileTag, a->fileTags - changedArtifact->fileTags) {
+ artifact->fileTags -= removedFileTag;
+ foreach (Artifact *parent, artifact->parents) {
+ if (parent->transformer && parent->transformer->rule->inputs.contains(removedFileTag)) {
+ // this parent has been created because of the removed filetag
+ removeArtifactAndExclusiveDependents(parent, &artifactsToRemove);
+ }
+ }
+ }
+ }
+ }
+
+ // Discard groups of the old product. Use the groups of the new one.
+ product->groups = changedProduct->groups;
+ product->properties = changedProduct->properties;
+
+ // apply rules for new artifacts
+ foreach (Artifact *artifact, addedArtifacts)
+ foreach (const FileTag &ft, artifact->fileTags)
+ artifactsPerFileTag[ft] += artifact;
+ RulesApplicator(product, artifactsPerFileTag, m_logger).applyAllRules();
+
+ addTargetArtifacts(product, artifactsPerFileTag, m_logger);
+
+ // parents of removed artifacts must update their transformers
+ foreach (Artifact *removedArtifact, artifactsToRemove)
+ foreach (Artifact *parent, removedArtifact->parents)
+ product->project->buildData->artifactsThatMustGetNewTransformers += parent;
+ product->project->buildData->updateNodesThatMustGetNewTransformer(m_logger);
+
+ // delete all removed artifacts physically from the disk
+ foreach (Artifact *artifact, artifactsToRemove) {
+ removeGeneratedArtifactFromDisk(artifact, m_logger);
+ delete artifact;
+ }
+}
+
+/**
+ * Removes the artifact and all the artifacts that depend exclusively on it.
+ * Example: if you remove a cpp artifact then the obj artifact is removed but
+ * not the resulting application (if there's more then one cpp artifact).
+ */
+void BuildGraphLoader::removeArtifactAndExclusiveDependents(Artifact *artifact,
+ QList<Artifact*> *removedArtifacts)
+{
+ if (removedArtifacts)
+ removedArtifacts->append(artifact);
+ foreach (Artifact *parent, artifact->parents) {
+ bool removeParent = false;
+ disconnect(parent, artifact, m_logger);
+ if (parent->children.isEmpty()) {
+ removeParent = true;
+ } else if (parent->transformer) {
+ artifact->project->buildData->artifactsThatMustGetNewTransformers += parent;
+ parent->transformer->inputs.remove(artifact);
+ removeParent = parent->transformer->inputs.isEmpty();
+ }
+ if (removeParent)
+ removeArtifactAndExclusiveDependents(parent, removedArtifacts);
+ }
+ artifact->project->buildData->removeArtifact(artifact, m_logger);
+}
+
+bool BuildGraphLoader::checkForPropertyChanges(const TransformerPtr &restoredTrafo,
+ const ResolvedProductPtr &freshProduct)
+{
+ PropertyFinder finder;
+ foreach (const Property &property, restoredTrafo->modulePropertiesUsedInPrepareScript) {
+ QVariant v;
+ if (property.value.type() == QVariant::List) {
+ v = finder.propertyValues(freshProduct->properties->value(), property.moduleName,
+ property.propertyName);
+ } else {
+ v = finder.propertyValue(freshProduct->properties->value(), property.moduleName,
+ property.propertyName);
+ }
+ if (property.value != v)
+ return true;
+ }
+ return false;
+}
+
+void addTargetArtifacts(const ResolvedProductPtr &product,
+ ArtifactsPerFileTagMap &artifactsPerFileTag, const Logger &logger)
+{
+ foreach (const FileTag &fileTag, product->fileTags) {
+ foreach (Artifact * const artifact, artifactsPerFileTag.value(fileTag)) {
+ if (artifact->artifactType == Artifact::Generated)
+ product->buildData->targetArtifacts += artifact;
+ }
+ }
+ if (product->buildData->targetArtifacts.isEmpty()) {
+ const QString msg = QString::fromLocal8Bit("No artifacts generated for product '%1'.");
+ logger.qbsDebug() << msg.arg(product->name);
+ }
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/buildgraph/buildgraph.h b/src/lib/buildgraph/buildgraph.h
index 79e80b612..70a5c9a29 100644
--- a/src/lib/buildgraph/buildgraph.h
+++ b/src/lib/buildgraph/buildgraph.h
@@ -26,22 +26,38 @@
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
-
#ifndef QBS_BUILDGRAPH_H
#define QBS_BUILDGRAPH_H
+#include "forward_decls.h"
+#include "rulesapplicator.h"
+
#include <language/forward_decls.h>
+#include <logging/logger.h>
#include <QScriptValue>
#include <QStringList>
namespace qbs {
+class SetupProjectParameters;
namespace Internal {
-class Artifact;
class Logger;
class ScriptEngine;
class ScriptPropertyObserver;
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const QString &dirPath,
+ const QString &fileName);
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const QString &filePath);
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const Artifact *artifact);
+
+Artifact *createArtifact(const ResolvedProductPtr &product,
+ const SourceArtifactConstPtr &sourceArtifact, const Logger &logger);
+void insertArtifact(const ResolvedProductPtr &product, Artifact *artifact, const Logger &logger);
+void addTargetArtifacts(const ResolvedProductPtr &product,
+ ArtifactsPerFileTagMap &artifactsPerFileTag, const Logger &logger);
+void dumpProductBuildData(const ResolvedProductConstPtr &product);
+
+
bool findPath(Artifact *u, Artifact *v, QList<Artifact*> &path);
void connect(Artifact *p, Artifact *c);
void loggedConnect(Artifact *u, Artifact *v, const Logger &logger);
@@ -63,6 +79,40 @@ QStringList toStringList(const T &artifactContainer)
return l;
}
+class BuildGraphLoader
+{
+public:
+ BuildGraphLoader(const Logger &logger);
+
+ class LoadResult
+ {
+ public:
+ LoadResult() : discardLoadedProject(false) {}
+
+ ResolvedProjectPtr newlyResolvedProject;
+ ResolvedProjectPtr loadedProject;
+ bool discardLoadedProject;
+ };
+
+ LoadResult load(const SetupProjectParameters &parameters,
+ const RulesEvaluationContextPtr &evalContext);
+
+private:
+ void trackProjectChanges(const SetupProjectParameters &parameters,
+ const QString &buildGraphFilePath,
+ const ResolvedProjectPtr &restoredProject);
+ void onProductRemoved(const ResolvedProductPtr &product);
+ void onProductChanged(const ResolvedProductPtr &product,
+ const ResolvedProductPtr &changedProduct);
+ void removeArtifactAndExclusiveDependents(Artifact *artifact,
+ QList<Artifact*> *removedArtifacts = 0);
+ bool checkForPropertyChanges(const TransformerPtr &restoredTrafo, const ResolvedProductPtr &freshProduct);
+
+ RulesEvaluationContextPtr m_evalContext;
+ LoadResult m_result;
+ Logger m_logger;
+};
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/buildgraph/buildgraph.pri b/src/lib/buildgraph/buildgraph.pri
index 80d10d659..d33e53861 100644
--- a/src/lib/buildgraph/buildgraph.pri
+++ b/src/lib/buildgraph/buildgraph.pri
@@ -18,8 +18,8 @@ SOURCES += \
$$PWD/productinstaller.cpp \
$$PWD/cycledetector.cpp \
$$PWD/rulesevaluationcontext.cpp \
- $$PWD/buildproduct.cpp \
- $$PWD/buildproject.cpp \
+ $$PWD/productbuilddata.cpp \
+ $$PWD/projectbuilddata.cpp \
$$PWD/rulesapplicator.cpp \
$$PWD/timestampsupdater.cpp
@@ -44,8 +44,8 @@ HEADERS += \
$$PWD/cycledetector.h \
$$PWD/forward_decls.h \
$$PWD/rulesevaluationcontext.h \
- $$PWD/buildproduct.h \
- $$PWD/buildproject.h \
+ $$PWD/productbuilddata.h \
+ $$PWD/projectbuilddata.h \
$$PWD/rulesapplicator.h \
$$PWD/timestampsupdater.h
diff --git a/src/lib/buildgraph/buildproduct.cpp b/src/lib/buildgraph/buildproduct.cpp
deleted file mode 100644
index a990d9547..000000000
--- a/src/lib/buildgraph/buildproduct.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 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 "buildproduct.h"
-
-#include "artifact.h"
-#include "buildproject.h"
-#include "rulegraph.h"
-#include "rulesevaluationcontext.h"
-#include <language/language.h>
-#include <logging/logger.h>
-#include <tools/error.h>
-#include <tools/persistence.h>
-#include <tools/qbsassert.h>
-
-namespace qbs {
-namespace Internal {
-
-BuildProduct::BuildProduct()
-{
-}
-
-BuildProduct::~BuildProduct()
-{
- qDeleteAll(artifacts);
-}
-
-static void internalDump(const BuildProduct *product, Artifact *artifact, QByteArray indent)
-{
- Artifact *artifactInProduct = product->lookupArtifact(artifact->filePath());
- if (artifactInProduct && artifactInProduct != artifact) {
- qFatal("\ntree corrupted. %p ('%s') resolves to %p ('%s')\n",
- artifact, qPrintable(artifact->filePath()), product->lookupArtifact(artifact->filePath()),
- qPrintable(product->lookupArtifact(artifact->filePath())->filePath()));
-
- }
- printf("%s", indent.constData());
- printf("Artifact (%p) ", artifact);
- printf("%s%s %s [%s]",
- qPrintable(QString(toString(artifact->buildState).at(0))),
- artifactInProduct ? "" : " SBS", // SBS == side-by-side artifact from other product
- qPrintable(artifact->filePath()),
- qPrintable(artifact->fileTags.toStringList().join(QLatin1String(", "))));
- printf("\n");
- indent.append(" ");
- foreach (Artifact *child, artifact->children) {
- internalDump(product, child, indent);
- }
-}
-
-void BuildProduct::dump() const
-{
- foreach (Artifact *artifact, artifacts)
- if (artifact->parents.isEmpty())
- internalDump(this, artifact, QByteArray());
-}
-
-const QList<RuleConstPtr> &BuildProduct::topSortedRules() const
-{
- if (m_topSortedRules.isEmpty()) {
- FileTags productFileTags = rProduct->fileTags;
- productFileTags += rProduct->additionalFileTags;
- RuleGraph ruleGraph;
- ruleGraph.build(rProduct->rules, productFileTags);
-// ruleGraph.dump();
- m_topSortedRules = ruleGraph.topSorted();
-// int i=0;
-// foreach (RulePtr r, m_topSortedRules)
-// qDebug() << ++i << r->toString() << (void*)r.data();
- }
- return m_topSortedRules;
-}
-
-Artifact *BuildProduct::lookupArtifact(const QString &dirPath, const QString &fileName) const
-{
- QList<Artifact *> artifacts = project->lookupArtifacts(dirPath, fileName);
- for (QList<Artifact *>::const_iterator it = artifacts.constBegin(); it != artifacts.constEnd(); ++it)
- if ((*it)->product == this)
- return *it;
- return 0;
-}
-
-Artifact *BuildProduct::lookupArtifact(const QString &filePath) const
-{
- QString dirPath, fileName;
- FileInfo::splitIntoDirectoryAndFileName(filePath, &dirPath, &fileName);
- return lookupArtifact(dirPath, fileName);
-}
-
-Artifact *BuildProduct::lookupArtifact(const Artifact *artifact) const
-{
- return lookupArtifact(artifact->dirPath(), artifact->fileName());
-}
-
-Artifact *BuildProduct::createArtifact(const SourceArtifactConstPtr &sourceArtifact,
- const Logger &logger)
-{
- Artifact *artifact = new Artifact(project);
- artifact->artifactType = Artifact::SourceFile;
- artifact->setFilePath(sourceArtifact->absoluteFilePath);
- artifact->fileTags = sourceArtifact->fileTags;
- artifact->properties = sourceArtifact->properties;
- insertArtifact(artifact, logger);
- return artifact;
-}
-
-void BuildProduct::insertArtifact(Artifact *artifact, const Logger &logger)
-{
- QBS_CHECK(!artifact->product);
- QBS_CHECK(!artifact->filePath().isEmpty());
- QBS_CHECK(!artifacts.contains(artifact));
-#ifdef QT_DEBUG
- foreach (const BuildProductPtr &otherProduct, project->buildProducts()) {
- if (otherProduct->lookupArtifact(artifact->filePath())) {
- if (artifact->artifactType == Artifact::Generated) {
- QString pl;
- pl.append(QString(" - %1 \n").arg(rProduct->name));
- foreach (const BuildProductPtr &p, project->buildProducts()) {
- if (p->lookupArtifact(artifact->filePath()))
- pl.append(QString(" - %1 \n").arg(p->rProduct->name));
- }
- throw Error(QString ("BUG: already inserted in this project: %1\n%2")
- .arg(artifact->filePath()).arg(pl));
- }
- }
- }
-#endif
- artifacts.insert(artifact);
- artifact->product = this;
- project->insertIntoArtifactLookupTable(artifact);
- project->markDirty();
-
- if (logger.traceEnabled()) {
- logger.qbsTrace() << QString::fromLocal8Bit("[BG] insert artifact '%1'")
- .arg(artifact->filePath());
- }
-}
-
-void BuildProduct::load(PersistentPool &pool)
-{
- // artifacts
- int i;
- pool.stream() >> i;
- artifacts.clear();
- for (; --i >= 0;) {
- Artifact *artifact = pool.idLoad<Artifact>();
- artifacts.insert(artifact);
- artifact->product = this;
- }
-
- // edges
- for (i = artifacts.count(); --i >= 0;) {
- Artifact *artifact = pool.idLoad<Artifact>();
- int k;
- pool.stream() >> k;
- artifact->parents.clear();
- artifact->parents.reserve(k);
- for (; --k >= 0;)
- artifact->parents.insert(pool.idLoad<Artifact>());
-
- pool.stream() >> k;
- artifact->children.clear();
- artifact->children.reserve(k);
- for (; --k >= 0;)
- artifact->children.insert(pool.idLoad<Artifact>());
-
- pool.stream() >> k;
- artifact->fileDependencies.clear();
- artifact->fileDependencies.reserve(k);
- for (; --k >= 0;)
- artifact->fileDependencies.insert(pool.idLoad<Artifact>());
- }
-
- // other data
- rProduct = pool.idLoadS<ResolvedProduct>();
- pool.loadContainer(targetArtifacts);
-
- pool.stream() >> i;
- dependencies.clear();
- dependencies.reserve(i);
- for (; --i >= 0;)
- dependencies += pool.idLoadS<BuildProduct>();
-}
-
-void BuildProduct::store(PersistentPool &pool) const
-{
- pool.stream() << artifacts.count();
-
- //artifacts
- for (ArtifactList::const_iterator i = artifacts.constBegin(); i != artifacts.constEnd(); ++i)
- pool.store(*i);
-
- // edges
- for (ArtifactList::const_iterator i = artifacts.constBegin(); i != artifacts.constEnd(); ++i) {
- Artifact * artifact = *i;
- pool.store(artifact);
-
- pool.stream() << artifact->parents.count();
- foreach (Artifact * n, artifact->parents)
- pool.store(n);
- pool.stream() << artifact->children.count();
- foreach (Artifact * n, artifact->children)
- pool.store(n);
- pool.stream() << artifact->fileDependencies.count();
- foreach (Artifact * n, artifact->fileDependencies)
- pool.store(n);
- }
-
- // other data
- pool.store(rProduct);
- pool.storeContainer(targetArtifacts);
- pool.storeContainer(dependencies);
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/buildgraph/buildproject.cpp b/src/lib/buildgraph/buildproject.cpp
deleted file mode 100644
index 0cc8a7f26..000000000
--- a/src/lib/buildgraph/buildproject.cpp
+++ /dev/null
@@ -1,803 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 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 "buildproject.h"
-
-#include "artifact.h"
-#include "buildgraph.h"
-#include "buildproduct.h"
-#include "command.h"
-#include "cycledetector.h"
-#include "rulesapplicator.h"
-#include "rulesevaluationcontext.h"
-#include "transformer.h"
-#include <language/language.h>
-#include <language/loader.h>
-#include <language/scriptengine.h>
-#include <logging/translator.h>
-#include <tools/error.h>
-#include <tools/persistence.h>
-#include <tools/propertyfinder.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/qbsassert.h>
-
-#include <QDir>
-#include <QFileInfo>
-
-namespace qbs {
-namespace Internal {
-
-BuildProject::BuildProject(const Logger &logger)
- : m_evalContext(0), m_dirty(false), m_logger(logger)
-{
-}
-
-BuildProject::~BuildProject()
-{
- qDeleteAll(m_dependencyArtifacts);
-}
-
-void BuildProject::store() const
-{
- if (!dirty()) {
- m_logger.qbsDebug() << "[BG] build graph is unchanged in project "
- << resolvedProject()->id() << ".";
- return;
- }
- const QString fileName = buildGraphFilePath();
- m_logger.qbsDebug() << "[BG] storing: " << fileName;
- PersistentPool pool(m_logger);
- PersistentPool::HeadData headData;
- headData.projectConfig = resolvedProject()->buildConfiguration();
- pool.setHeadData(headData);
- pool.setupWriteStream(fileName);
- store(pool);
- m_dirty = false;
-}
-
-QString BuildProject::deriveBuildGraphFilePath(const QString &buildDir, const QString &projectId)
-{
- return buildDir + QLatin1Char('/') + projectId + QLatin1String(".bg");
-}
-
-QString BuildProject::buildGraphFilePath() const
-{
- return deriveBuildGraphFilePath(resolvedProject()->buildDirectory, resolvedProject()->id());
-}
-
-void BuildProject::setEvaluationContext(const RulesEvaluationContextPtr &evalContext)
-{
- m_evalContext = evalContext;
-}
-
-ResolvedProjectPtr BuildProject::resolvedProject() const
-{
- return m_resolvedProject;
-}
-
-QSet<BuildProductPtr> BuildProject::buildProducts() const
-{
- return m_buildProducts;
-}
-
-bool BuildProject::dirty() const
-{
- return m_dirty;
-}
-
-void BuildProject::insertIntoArtifactLookupTable(Artifact *artifact)
-{
- QList<Artifact *> &lst = m_artifactLookupTable[artifact->fileName()][artifact->dirPath()];
- if (!lst.contains(artifact))
- lst.append(artifact);
-}
-
-void BuildProject::removeFromArtifactLookupTable(Artifact *artifact)
-{
- m_artifactLookupTable[artifact->fileName()][artifact->dirPath()].removeOne(artifact);
-}
-
-QList<Artifact *> BuildProject::lookupArtifacts(const QString &filePath) const
-{
- QString dirPath, fileName;
- FileInfo::splitIntoDirectoryAndFileName(filePath, &dirPath, &fileName);
- return lookupArtifacts(dirPath, fileName);
-}
-
-QList<Artifact *> BuildProject::lookupArtifacts(const QString &dirPath, const QString &fileName) const
-{
- QList<Artifact *> result = m_artifactLookupTable.value(fileName).value(dirPath);
- return result;
-}
-
-QList<Artifact *> BuildProject::lookupArtifacts(const Artifact *artifact) const
-{
- return lookupArtifacts(artifact->dirPath(), artifact->fileName());
-}
-
-void BuildProject::insertFileDependency(Artifact *artifact)
-{
- QBS_CHECK(artifact->artifactType == Artifact::FileDependency);
- m_dependencyArtifacts += artifact;
- insertIntoArtifactLookupTable(artifact);
-}
-
-static bool commandsEqual(const TransformerConstPtr &t1, const TransformerConstPtr &t2)
-{
- if (t1->commands.count() != t2->commands.count())
- return false;
- for (int i = 0; i < t1->commands.count(); ++i)
- if (!t1->commands.at(i)->equals(t2->commands.at(i)))
- return false;
- return true;
-}
-
-/**
- * Rescues the following data from the other project to this project:
- * - dependencies between artifacts,
- * - time stamps of artifacts, if their commands have not changed.
- */
-void BuildProject::rescueData(const BuildProjectConstPtr &other)
-{
- QHash<QString, BuildProductConstPtr> otherProductsByName;
- foreach (const BuildProductConstPtr &product, other->m_buildProducts)
- otherProductsByName.insert(product->rProduct->name, product);
-
- foreach (const BuildProductPtr &product, m_buildProducts) {
- BuildProductConstPtr otherProduct = otherProductsByName.value(product->rProduct->name);
- if (!otherProduct)
- continue;
-
- if (m_logger.traceEnabled()) {
- m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] rescue data of "
- "product '%1'").arg(product->rProduct->name);
- }
-
- foreach (Artifact *artifact, product->artifacts) {
- if (m_logger.traceEnabled()) {
- m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] artifact '%1'")
- .arg(artifact->fileName());
- }
-
- Artifact *otherArtifact = otherProduct->lookupArtifact(artifact);
- if (!otherArtifact || !otherArtifact->transformer) {
- if (m_logger.traceEnabled())
- m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] no transformer data");
- continue;
- }
-
- if (artifact->transformer
- && !commandsEqual(artifact->transformer, otherArtifact->transformer)) {
- if (m_logger.traceEnabled())
- m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] artifact invalidated");
- continue;
- }
- artifact->timestamp = otherArtifact->timestamp;
-
- foreach (Artifact *otherChild, otherArtifact->children) {
- // skip transform edges
- if (otherArtifact->transformer->inputs.contains(otherChild))
- continue;
-
- QList<Artifact *> children = lookupArtifacts(otherChild);
- foreach (Artifact *child, children) {
- if (!artifact->children.contains(child))
- safeConnect(artifact, child, m_logger);
- }
- }
- }
- }
-}
-
-void BuildProject::markDirty()
-{
- m_dirty = true;
-}
-
-void BuildProject::addBuildProduct(const BuildProductPtr &product)
-{
- m_buildProducts.insert(product);
-}
-
-void BuildProject::setResolvedProject(const ResolvedProjectPtr &resolvedProject)
-{
- m_resolvedProject = resolvedProject;
-}
-
-void BuildProject::removeArtifact(Artifact *artifact)
-{
- if (m_logger.traceEnabled())
- m_logger.qbsTrace() << "[BG] remove artifact " << relativeArtifactFileName(artifact);
-
- removeGeneratedArtifactFromDisk(artifact, m_logger);
- artifact->product->artifacts.remove(artifact);
- removeFromArtifactLookupTable(artifact);
- artifact->product->targetArtifacts.remove(artifact);
- foreach (Artifact *parent, artifact->parents) {
- parent->children.remove(artifact);
- if (parent->transformer) {
- parent->transformer->inputs.remove(artifact);
- m_artifactsThatMustGetNewTransformers += parent;
- }
- }
- foreach (Artifact *child, artifact->children)
- child->parents.remove(artifact);
- artifact->children.clear();
- artifact->parents.clear();
- markDirty();
-}
-
-void BuildProject::updateNodesThatMustGetNewTransformer()
-{
- RulesEvaluationContext::Scope s(evaluationContext().data());
- foreach (Artifact *artifact, m_artifactsThatMustGetNewTransformers)
- updateNodeThatMustGetNewTransformer(artifact);
- m_artifactsThatMustGetNewTransformers.clear();
-}
-
-void BuildProject::updateNodeThatMustGetNewTransformer(Artifact *artifact)
-{
- QBS_CHECK(artifact->transformer);
-
- if (m_logger.debugEnabled()) {
- m_logger.qbsDebug() << "[BG] updating transformer for "
- << relativeArtifactFileName(artifact);
- }
-
- removeGeneratedArtifactFromDisk(artifact, m_logger);
-
- const RuleConstPtr rule = artifact->transformer->rule;
- markDirty();
- artifact->transformer = TransformerPtr();
-
- ArtifactsPerFileTagMap artifactsPerFileTag;
- foreach (Artifact *input, artifact->children) {
- foreach (const FileTag &fileTag, input->fileTags)
- artifactsPerFileTag[fileTag] += input;
- }
- RulesApplicator rulesApplier(artifact->product, artifactsPerFileTag, m_logger);
- rulesApplier.applyRule(rule);
-}
-
-void BuildProject::load(PersistentPool &pool)
-{
- TimedActivityLogger logger(m_logger, QLatin1String("Loading project from disk."));
- m_resolvedProject = pool.idLoadS<ResolvedProject>();
- foreach (const ResolvedProductPtr &product, m_resolvedProject->products)
- product->project = m_resolvedProject;
-
- int count;
- pool.stream() >> count;
- for (; --count >= 0;) {
- BuildProductPtr product = pool.idLoadS<BuildProduct>();
- foreach (Artifact *artifact, product->artifacts) {
- artifact->project = this;
- insertIntoArtifactLookupTable(artifact);
- }
- addBuildProduct(product);
- }
-
- pool.stream() >> count;
- m_dependencyArtifacts.clear();
- m_dependencyArtifacts.reserve(count);
- for (; --count >= 0;) {
- Artifact *artifact = pool.idLoad<Artifact>();
- artifact->project = this;
- insertFileDependency(artifact);
- }
-}
-
-void BuildProject::store(PersistentPool &pool) const
-{
- pool.store(m_resolvedProject);
- pool.storeContainer(m_buildProducts);
- pool.storeContainer(m_dependencyArtifacts);
-}
-
-
-BuildProjectResolver::BuildProjectResolver(const Logger &logger) : m_logger(logger)
-{
-}
-
-BuildProjectPtr BuildProjectResolver::resolveProject(const ResolvedProjectPtr &resolvedProject,
- const RulesEvaluationContextPtr &evalContext)
-{
- m_productCache.clear();
- m_project = BuildProjectPtr(new BuildProject(m_logger));
- m_project->setEvaluationContext(evalContext);
- m_project->setResolvedProject(resolvedProject);
- evalContext->initializeObserver(Tr::tr("Setting up build graph for configuration %1")
- .arg(resolvedProject->id()), resolvedProject->products.count());
- foreach (ResolvedProductPtr rProduct, resolvedProject->products) {
- if (rProduct->enabled)
- resolveProduct(rProduct);
- evalContext->incrementProgressValue();
- }
- CycleDetector(m_logger).visitProject(m_project);
- return m_project;
-}
-
-void BuildProjectResolver::resolveProductsForExistingProject(const BuildProjectPtr &project,
- const QList<ResolvedProductPtr> &freshProducts)
-{
- m_project = project;
- foreach (const ResolvedProductPtr &product, freshProducts)
- resolveProduct(product);
-}
-
-static void addTargetArtifacts(const BuildProductPtr &product,
- ArtifactsPerFileTagMap &artifactsPerFileTag, const Logger &logger)
-{
- foreach (const FileTag &fileTag, product->rProduct->fileTags) {
- foreach (Artifact * const artifact, artifactsPerFileTag.value(fileTag)) {
- if (artifact->artifactType == Artifact::Generated)
- product->targetArtifacts += artifact;
- }
- }
- if (product->targetArtifacts.isEmpty()) {
- const QString msg = QString::fromLocal8Bit("No artifacts generated for product '%1'.");
- logger.qbsDebug() << msg.arg(product->rProduct->name);
- }
-}
-
-BuildProductPtr BuildProjectResolver::resolveProduct(const ResolvedProductPtr &rProduct)
-{
- BuildProductPtr product = m_productCache.value(rProduct);
- if (product)
- return product;
-
- evalContext()->checkForCancelation();
-
- product = BuildProduct::create();
- m_productCache.insert(rProduct, product);
- product->project = m_project;
- product->rProduct = rProduct;
- ArtifactsPerFileTagMap artifactsPerFileTag;
-
- foreach (ResolvedProductPtr dependency, rProduct->dependencies) {
- if (dependency == rProduct)
- throw Error(Tr::tr("circular using"));
- if (!dependency->enabled) {
- QString msg = Tr::tr("Product '%1' depends on '%2' but '%2' is disabled.");
- throw Error(msg.arg(rProduct->name, dependency->name));
- }
- BuildProductPtr referencedProduct = resolveProduct(dependency);
- product->dependencies.append(referencedProduct);
- }
-
- //add qbsFile artifact
- Artifact *qbsFileArtifact = product->lookupArtifact(rProduct->location.fileName);
- if (!qbsFileArtifact) {
- qbsFileArtifact = new Artifact(m_project.data());
- qbsFileArtifact->artifactType = Artifact::SourceFile;
- qbsFileArtifact->setFilePath(rProduct->location.fileName);
- qbsFileArtifact->properties = rProduct->properties;
- product->insertArtifact(qbsFileArtifact, m_logger);
- }
- qbsFileArtifact->fileTags.insert("qbs");
- artifactsPerFileTag["qbs"].insert(qbsFileArtifact);
-
- // read sources
- foreach (const SourceArtifactConstPtr &sourceArtifact, rProduct->allEnabledFiles()) {
- QString filePath = sourceArtifact->absoluteFilePath;
- if (product->lookupArtifact(filePath))
- continue; // ignore duplicate artifacts
-
- Artifact *artifact = product->createArtifact(sourceArtifact, m_logger);
- foreach (const FileTag &fileTag, artifact->fileTags)
- artifactsPerFileTag[fileTag].insert(artifact);
- }
-
- // read manually added transformers
- foreach (const ResolvedTransformer::Ptr rtrafo, rProduct->transformers) {
- ArtifactList inputArtifacts;
- foreach (const QString &inputFileName, rtrafo->inputs) {
- Artifact *artifact = product->lookupArtifact(inputFileName);
- if (!artifact)
- throw Error(QString("Can't find artifact '%0' in the list of source files.").arg(inputFileName));
- inputArtifacts += artifact;
- }
- TransformerPtr transformer = Transformer::create();
- transformer->inputs = inputArtifacts;
- const RulePtr rule = Rule::create();
- rule->jsImports = rtrafo->jsImports;
- ResolvedModulePtr module = ResolvedModule::create();
- module->name = rtrafo->module->name;
- rule->module = module;
- rule->script = rtrafo->transform;
- foreach (const SourceArtifactConstPtr &sourceArtifact, rtrafo->outputs) {
- Artifact *outputArtifact = product->createArtifact(sourceArtifact, m_logger);
- outputArtifact->artifactType = Artifact::Generated;
- outputArtifact->transformer = transformer;
- transformer->outputs += outputArtifact;
- product->targetArtifacts += outputArtifact;
- foreach (Artifact *inputArtifact, inputArtifacts)
- safeConnect(outputArtifact, inputArtifact, m_logger);
- foreach (const FileTag &fileTag, outputArtifact->fileTags)
- artifactsPerFileTag[fileTag].insert(outputArtifact);
-
- RuleArtifactPtr ruleArtifact = RuleArtifact::create();
- ruleArtifact->fileName = outputArtifact->filePath();
- ruleArtifact->fileTags = outputArtifact->fileTags;
- rule->artifacts += ruleArtifact;
- }
- transformer->rule = rule;
-
- RulesEvaluationContext::Scope s(evalContext().data());
- setupScriptEngineForProduct(engine(), rProduct, transformer->rule, scope());
- transformer->setupInputs(engine(), scope());
- transformer->setupOutputs(engine(), scope());
- transformer->createCommands(rtrafo->transform, evalContext());
- if (transformer->commands.isEmpty())
- throw Error(QString("There's a transformer without commands."), rtrafo->transform->location);
- }
-
- RulesApplicator(product.data(), artifactsPerFileTag, m_logger).applyAllRules();
- addTargetArtifacts(product, artifactsPerFileTag, m_logger);
- m_project->addBuildProduct(product);
- return product;
-}
-
-RulesEvaluationContextPtr BuildProjectResolver::evalContext() const
-{
- return m_project->evaluationContext();
-}
-
-ScriptEngine *BuildProjectResolver::engine() const
-{
- return evalContext()->engine();
-}
-
-QScriptValue BuildProjectResolver::scope() const
-{
- return evalContext()->scope();
-}
-
-
-static bool isConfigCompatible(const QVariantMap &userCfg, const QVariantMap &projectCfg)
-{
- QVariantMap::const_iterator it = userCfg.begin();
- for (; it != userCfg.end(); ++it) {
- if (it.value().type() == QVariant::Map) {
- if (!isConfigCompatible(it.value().toMap(), projectCfg.value(it.key()).toMap()))
- return false;
- } else {
- QVariant value = projectCfg.value(it.key());
- if (!value.isNull() && value != it.value()) {
- return false;
- }
- }
- }
- return true;
-}
-
-BuildProjectLoader::BuildProjectLoader(const Logger &logger) : m_logger(logger)
-{
-}
-
-BuildProjectLoader::LoadResult BuildProjectLoader::load(const SetupProjectParameters &parameters,
- const RulesEvaluationContextPtr &evalContext)
-{
- m_result = LoadResult();
- m_evalContext = evalContext;
-
- const QString projectId = ResolvedProject::deriveId(parameters.buildConfiguration);
- const QString buildDir = ResolvedProject::deriveBuildDirectory(parameters.buildRoot, projectId);
- const QString buildGraphFilePath = BuildProject::deriveBuildGraphFilePath(buildDir, projectId);
-
- PersistentPool pool(m_logger);
- m_logger.qbsDebug() << "[BG] trying to load: " << buildGraphFilePath;
- if (!pool.load(buildGraphFilePath))
- return m_result;
- if (!isConfigCompatible(parameters.buildConfiguration, pool.headData().projectConfig)) {
- m_logger.qbsDebug() << "[BG] Cannot use stored build graph: "
- "Incompatible project configuration.";
- return m_result;
- }
-
- const BuildProjectPtr project = BuildProjectPtr(new BuildProject(m_logger));
- project->setEvaluationContext(evalContext);
- TimedActivityLogger loadLogger(m_logger, QLatin1String("Loading build graph"),
- QLatin1String("[BG] "));
- project->load(pool);
- if (QFileInfo(project->resolvedProject()->location.fileName)
- != QFileInfo(parameters.projectFilePath)) {
- QString errorMessage = Tr::tr("Stored build graph is for project file '%1', but "
- "input file is '%2'. ")
- .arg(QDir::toNativeSeparators(project->resolvedProject()->location.fileName),
- QDir::toNativeSeparators(parameters.projectFilePath));
- if (!parameters.ignoreDifferentProjectFilePath) {
- errorMessage += Tr::tr("Aborting.");
- throw Error(errorMessage);
- }
-
- errorMessage += Tr::tr("Ignoring.");
- m_logger.qbsWarning() << errorMessage;
-
- // Okay, let's assume it's the same project anyway (the source dir might have moved).
- project->resolvedProject()->location.fileName = parameters.projectFilePath;
- }
- foreach (const BuildProductPtr &bp, project->buildProducts())
- bp->project = project;
- project->resolvedProject()->location = CodeLocation(parameters.projectFilePath, 1, 1);
- project->resolvedProject()->setBuildConfiguration(pool.headData().projectConfig);
- project->resolvedProject()->buildDirectory = buildDir;
- m_result.loadedProject = project;
- loadLogger.finishActivity();
- trackProjectChanges(parameters, buildGraphFilePath, project);
- return m_result;
-}
-
-void BuildProjectLoader::trackProjectChanges(const SetupProjectParameters &parameters, const QString &buildGraphFilePath,
- const BuildProjectPtr &restoredProject)
-{
- const FileInfo bgfi(buildGraphFilePath);
- const bool projectFileChanged
- = bgfi.lastModified() < FileInfo(parameters.projectFilePath).lastModified();
-
- bool referencedProductRemoved = false;
- QList<BuildProductPtr> changedProducts;
- foreach (const BuildProductPtr &product, restoredProject->buildProducts()) {
- const ResolvedProductConstPtr &resolvedProduct = product->rProduct;
- const FileInfo pfi(resolvedProduct->location.fileName);
- if (!pfi.exists()) {
- referencedProductRemoved = true;
- } else if (bgfi.lastModified() < pfi.lastModified()) {
- changedProducts += product;
- } else {
- foreach (const GroupPtr &group, resolvedProduct->groups) {
- if (!group->wildcards)
- continue;
- const QSet<QString> files
- = group->wildcards->expandPatterns(group, resolvedProduct->sourceDirectory);
- QSet<QString> wcFiles;
- foreach (const SourceArtifactConstPtr &sourceArtifact, group->wildcards->files)
- wcFiles += sourceArtifact->absoluteFilePath;
- if (files == wcFiles)
- continue;
- changedProducts += product;
- break;
- }
- }
- }
-
- if (!projectFileChanged && !referencedProductRemoved && changedProducts.isEmpty())
- return;
-
- Loader ldr(m_evalContext->engine(), m_logger);
- ldr.setSearchPaths(parameters.searchPaths);
- const ResolvedProjectPtr freshProject = ldr.loadProject(parameters);
- m_result.changedResolvedProject = freshProject;
-
- QMap<QString, ResolvedProductPtr> freshProductsByName;
- foreach (const ResolvedProductPtr &cp, freshProject->products)
- freshProductsByName.insert(cp->name, cp);
-
- QSet<TransformerPtr> seenTransformers;
- foreach (const BuildProductPtr &product, restoredProject->buildProducts()) {
- foreach (Artifact *artifact, product->artifacts) {
- if (!artifact->transformer || seenTransformers.contains(artifact->transformer))
- continue;
- seenTransformers.insert(artifact->transformer);
- ResolvedProductPtr freshProduct = freshProductsByName.value(product->rProduct->name);
- if (freshProduct && checkForPropertyChanges(artifact->transformer, freshProduct)) {
- m_result.discardLoadedProject = true;
- return;
- }
- }
- }
-
- foreach (const BuildProductPtr &product, changedProducts) {
- ResolvedProductPtr freshProduct = freshProductsByName.value(product->rProduct->name);
- if (!freshProduct)
- continue;
- onProductChanged(product, freshProduct);
- if (m_result.discardLoadedProject)
- return;
- }
-
- QSet<QString> oldProductNames, newProductNames;
- foreach (const ResolvedProductConstPtr &product, restoredProject->resolvedProject()->products)
- oldProductNames += product->name;
- foreach (const ResolvedProductConstPtr &product, freshProject->products)
- newProductNames += product->name;
-
- const QSet<QString> removedProductsNames = oldProductNames - newProductNames;
- if (!removedProductsNames.isEmpty()) {
- foreach (const BuildProductPtr &product, restoredProject->buildProducts()) {
- if (removedProductsNames.contains(product->rProduct->name))
- onProductRemoved(product);
- }
- }
-
- const QSet<QString> addedProductNames = newProductNames - oldProductNames;
- QList<ResolvedProductPtr> addedProducts;
- foreach (const QString &productName, addedProductNames) {
- const ResolvedProductPtr &freshProduct = freshProductsByName.value(productName);
- QBS_ASSERT(freshProduct, continue);
- addedProducts.append(freshProduct);
- }
- if (!addedProducts.isEmpty()) {
- ResolvedProjectPtr restoredResolvedProject = restoredProject->resolvedProject();
- foreach (const ResolvedProductPtr &product, addedProducts) {
- product->project = restoredResolvedProject;
- restoredResolvedProject->products.append(product);
- }
- BuildProjectResolver bpr(m_logger);
- bpr.resolveProductsForExistingProject(restoredProject, addedProducts);
- }
-
- CycleDetector(m_logger).visitProject(restoredProject);
-}
-
-void BuildProjectLoader::onProductRemoved(const BuildProductPtr &product)
-{
- m_logger.qbsDebug() << "[BG] product '" << product->rProduct->name << "' removed.";
-
- product->project->markDirty();
- product->project->m_buildProducts.remove(product);
-
- // delete all removed artifacts physically from the disk
- foreach (Artifact *artifact, product->artifacts) {
- artifact->disconnectAll(m_logger);
- removeGeneratedArtifactFromDisk(artifact, m_logger);
- }
-}
-
-void BuildProjectLoader::onProductChanged(const BuildProductPtr &product,
- const ResolvedProductPtr &changedProduct)
-{
- m_logger.qbsDebug() << "[BG] product '" << product->rProduct->name << "' changed.";
-
- ArtifactsPerFileTagMap artifactsPerFileTag;
- QSet<SourceArtifactConstPtr> addedSourceArtifacts;
- QList<Artifact *> addedArtifacts, artifactsToRemove;
- QHash<QString, SourceArtifactConstPtr> oldArtifacts, newArtifacts;
-
- const QList<SourceArtifactPtr> oldProductAllFiles = product->rProduct->allEnabledFiles();
- foreach (const SourceArtifactConstPtr &a, oldProductAllFiles)
- oldArtifacts.insert(a->absoluteFilePath, a);
- foreach (const SourceArtifactPtr &a, changedProduct->allEnabledFiles()) {
- newArtifacts.insert(a->absoluteFilePath, a);
- if (!oldArtifacts.contains(a->absoluteFilePath)) {
- // artifact added
- m_logger.qbsDebug() << "[BG] artifact '" << a->absoluteFilePath
- << "' added to product " << product->rProduct->name;
- addedArtifacts += product->createArtifact(a, m_logger);
- addedSourceArtifacts += a;
- }
- }
-
- foreach (const SourceArtifactPtr &a, oldProductAllFiles) {
- const SourceArtifactConstPtr changedArtifact = newArtifacts.value(a->absoluteFilePath);
- if (!changedArtifact) {
- // artifact removed
- m_logger.qbsDebug() << "[BG] artifact '" << a->absoluteFilePath
- << "' removed from product " << product->rProduct->name;
- Artifact *artifact = product->lookupArtifact(a->absoluteFilePath);
- QBS_CHECK(artifact);
- removeArtifactAndExclusiveDependents(artifact, &artifactsToRemove);
- continue;
- }
- if (changedArtifact->fileTags != a->fileTags) {
- // artifact's filetags have changed
- m_logger.qbsDebug() << "[BG] filetags have changed for artifact '"
- << a->absoluteFilePath << "' from " << a->fileTags << " to "
- << changedArtifact->fileTags;
- Artifact *artifact = product->lookupArtifact(a->absoluteFilePath);
- QBS_CHECK(artifact);
-
- // handle added filetags
- foreach (const FileTag &addedFileTag, changedArtifact->fileTags - a->fileTags)
- artifactsPerFileTag[addedFileTag] += artifact;
-
- // handle removed filetags
- foreach (const FileTag &removedFileTag, a->fileTags - changedArtifact->fileTags) {
- artifact->fileTags -= removedFileTag;
- foreach (Artifact *parent, artifact->parents) {
- if (parent->transformer && parent->transformer->rule->inputs.contains(removedFileTag)) {
- // this parent has been created because of the removed filetag
- removeArtifactAndExclusiveDependents(parent, &artifactsToRemove);
- }
- }
- }
- }
- }
-
- // Discard groups of the old product. Use the groups of the new one.
- product->rProduct->groups = changedProduct->groups;
- product->rProduct->properties = changedProduct->properties;
-
- // apply rules for new artifacts
- foreach (Artifact *artifact, addedArtifacts)
- foreach (const FileTag &ft, artifact->fileTags)
- artifactsPerFileTag[ft] += artifact;
- RulesApplicator(product.data(), artifactsPerFileTag, m_logger).applyAllRules();
-
- addTargetArtifacts(product, artifactsPerFileTag, m_logger);
-
- // parents of removed artifacts must update their transformers
- foreach (Artifact *removedArtifact, artifactsToRemove)
- foreach (Artifact *parent, removedArtifact->parents)
- product->project->addToArtifactsThatMustGetNewTransformers(parent);
- product->project->updateNodesThatMustGetNewTransformer();
-
- // delete all removed artifacts physically from the disk
- foreach (Artifact *artifact, artifactsToRemove) {
- removeGeneratedArtifactFromDisk(artifact, m_logger);
- delete artifact;
- }
-}
-
-/**
- * Removes the artifact and all the artifacts that depend exclusively on it.
- * Example: if you remove a cpp artifact then the obj artifact is removed but
- * not the resulting application (if there's more then one cpp artifact).
- */
-void BuildProjectLoader::removeArtifactAndExclusiveDependents(Artifact *artifact,
- QList<Artifact*> *removedArtifacts)
-{
- if (removedArtifacts)
- removedArtifacts->append(artifact);
- foreach (Artifact *parent, artifact->parents) {
- bool removeParent = false;
- disconnect(parent, artifact, m_logger);
- if (parent->children.isEmpty()) {
- removeParent = true;
- } else if (parent->transformer) {
- artifact->project->addToArtifactsThatMustGetNewTransformers(parent);
- parent->transformer->inputs.remove(artifact);
- removeParent = parent->transformer->inputs.isEmpty();
- }
- if (removeParent)
- removeArtifactAndExclusiveDependents(parent, removedArtifacts);
- }
- artifact->project->removeArtifact(artifact);
-}
-
-bool BuildProjectLoader::checkForPropertyChanges(const TransformerPtr &restoredTrafo,
- const ResolvedProductPtr &freshProduct)
-{
- PropertyFinder finder;
- foreach (const Property &property, restoredTrafo->modulePropertiesUsedInPrepareScript) {
- QVariant v;
- if (property.value.type() == QVariant::List) {
- v = finder.propertyValues(freshProduct->properties->value(), property.moduleName,
- property.propertyName);
- } else {
- v = finder.propertyValue(freshProduct->properties->value(), property.moduleName,
- property.propertyName);
- }
- if (property.value != v)
- return true;
- }
- return false;
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/buildgraph/buildproject.h b/src/lib/buildgraph/buildproject.h
deleted file mode 100644
index 76fa3439f..000000000
--- a/src/lib/buildgraph/buildproject.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 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_BUILDPROJECT_H
-#define QBS_BUILDPROJECT_H
-
-#include "artifactlist.h"
-#include "forward_decls.h"
-#include <language/forward_decls.h>
-#include <logging/logger.h>
-#include <tools/persistentobject.h>
-
-#include <QHash>
-#include <QList>
-#include <QScriptValue>
-#include <QSet>
-#include <QString>
-#include <QStringList>
-#include <QVariantMap>
-
-namespace qbs {
-class Settings;
-class SetupProjectParameters;
-namespace Internal {
-class BuildProjectLoader;
-class BuildProjectResolver;
-class ScriptEngine;
-
-class BuildProject : public PersistentObject
-{
- friend class BuildProjectLoader;
- friend class BuildProjectResolver;
-public:
- BuildProject(const Logger &logger);
- ~BuildProject();
-
- void store() const;
- static QString deriveBuildGraphFilePath(const QString &buildDir, const QString &projectId);
- QString buildGraphFilePath() const;
-
- void setEvaluationContext(const RulesEvaluationContextPtr &evalContext);
- RulesEvaluationContextPtr evaluationContext() const { return m_evalContext; }
-
- ResolvedProjectPtr resolvedProject() const;
- QSet<BuildProductPtr> buildProducts() const;
- bool dirty() const;
- void markDirty();
- void insertIntoArtifactLookupTable(Artifact *artifact);
- void removeFromArtifactLookupTable(Artifact *artifact);
- QList<Artifact *> lookupArtifacts(const QString &filePath) const;
- QList<Artifact *> lookupArtifacts(const QString &dirPath, const QString &fileName) const;
- QList<Artifact *> lookupArtifacts(const Artifact *artifact) const;
- void insertFileDependency(Artifact *artifact);
- void rescueData(const BuildProjectConstPtr &other);
- void removeArtifact(Artifact *artifact);
- void updateNodesThatMustGetNewTransformer();
- void removeFromArtifactsThatMustGetNewTransformers(Artifact *a) {
- m_artifactsThatMustGetNewTransformers -= a;
- }
- void addToArtifactsThatMustGetNewTransformers(Artifact *a) {
- m_artifactsThatMustGetNewTransformers += a;
- }
-
-private:
- void load(PersistentPool &pool);
- void store(PersistentPool &pool) const;
- void addBuildProduct(const BuildProductPtr &product);
- void setResolvedProject(const ResolvedProjectPtr &resolvedProject);
- void updateNodeThatMustGetNewTransformer(Artifact *artifact);
-
-private:
- RulesEvaluationContextPtr m_evalContext;
- ResolvedProjectPtr m_resolvedProject;
- QSet<BuildProductPtr> m_buildProducts;
- ArtifactList m_dependencyArtifacts;
- QHash<QString, QHash<QString, QList<Artifact *> > > m_artifactLookupTable;
- QSet<Artifact *> m_artifactsThatMustGetNewTransformers;
- mutable bool m_dirty;
- Logger m_logger;
-};
-
-
-class BuildProjectResolver
-{
-public:
- BuildProjectResolver(const Logger &logger);
- BuildProjectPtr resolveProject(const ResolvedProjectPtr &resolvedProject,
- const RulesEvaluationContextPtr &evalContext);
- void resolveProductsForExistingProject(const BuildProjectPtr &project,
- const QList<ResolvedProductPtr> &freshProducts);
-
-private:
- BuildProductPtr resolveProduct(const ResolvedProductPtr &rProduct);
- RulesEvaluationContextPtr evalContext() const;
- ScriptEngine *engine() const;
- QScriptValue scope() const;
-
- BuildProjectPtr m_project;
- QHash<ResolvedProductPtr, BuildProductPtr> m_productCache;
- Logger m_logger;
-};
-
-
-class BuildProjectLoader
-{
-public:
- BuildProjectLoader(const Logger &logger);
-
- class LoadResult
- {
- public:
- LoadResult() : discardLoadedProject(false) {}
-
- ResolvedProjectPtr changedResolvedProject;
- BuildProjectPtr loadedProject;
- bool discardLoadedProject;
- };
-
- LoadResult load(const SetupProjectParameters &parameters,
- const RulesEvaluationContextPtr &evalContext);
-
-private:
- void trackProjectChanges(const SetupProjectParameters &parameters,
- const QString &buildGraphFilePath,
- const BuildProjectPtr &restoredProject);
- void onProductRemoved(const BuildProductPtr &product);
- void onProductChanged(const BuildProductPtr &product,
- const ResolvedProductPtr &changedProduct);
- void removeArtifactAndExclusiveDependents(Artifact *artifact,
- QList<Artifact*> *removedArtifacts = 0);
- bool checkForPropertyChanges(const TransformerPtr &restoredTrafo, const ResolvedProductPtr &freshProduct);
-
- RulesEvaluationContextPtr m_evalContext;
- LoadResult m_result;
- Logger m_logger;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // QBS_BUILDPROJECT_H
diff --git a/src/lib/buildgraph/cycledetector.cpp b/src/lib/buildgraph/cycledetector.cpp
index 15b113721..1e9dcdb9d 100644
--- a/src/lib/buildgraph/cycledetector.cpp
+++ b/src/lib/buildgraph/cycledetector.cpp
@@ -30,8 +30,6 @@
#include "artifact.h"
#include "buildgraph.h"
-#include "buildproject.h"
-#include "rulesevaluationcontext.h"
#include <language/language.h>
#include <logging/translator.h>
@@ -45,10 +43,10 @@ CycleDetector::CycleDetector(const Logger &logger)
{
}
-void CycleDetector::visitProject(const BuildProjectConstPtr &project)
+void CycleDetector::visitProject(const ResolvedProjectConstPtr &project)
{
const QString description = QString::fromLocal8Bit("Cycle detection for project '%1'")
- .arg(project->resolvedProject()->id());
+ .arg(project->id());
TimedActivityLogger timeLogger(m_logger, description, QLatin1String("[BG] "), LoggerTrace);
ArtifactVisitor::visitProject(project);
}
diff --git a/src/lib/buildgraph/cycledetector.h b/src/lib/buildgraph/cycledetector.h
index 9f47b03de..61ddac0b1 100644
--- a/src/lib/buildgraph/cycledetector.h
+++ b/src/lib/buildgraph/cycledetector.h
@@ -42,7 +42,7 @@ class CycleDetector : public ArtifactVisitor
public:
CycleDetector(const Logger &logger);
- void visitProject(const BuildProjectConstPtr &project);
+ void visitProject(const ResolvedProjectConstPtr &project);
void visitArtifact(Artifact *artifact);
private:
diff --git a/src/lib/buildgraph/executor.cpp b/src/lib/buildgraph/executor.cpp
index 32ab2278b..81072e111 100644
--- a/src/lib/buildgraph/executor.cpp
+++ b/src/lib/buildgraph/executor.cpp
@@ -31,8 +31,8 @@
#include "artifactvisitor.h"
#include "automoc.h"
#include "buildgraph.h"
-#include "buildproduct.h"
-#include "buildproject.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
#include "cycledetector.h"
#include "executorjob.h"
#include "inputartifactscanner.h"
@@ -157,7 +157,7 @@ void Executor::build()
}
}
-void Executor::setProducts(const QList<BuildProductPtr> &productsToBuild)
+void Executor::setProducts(const QList<ResolvedProductPtr> &productsToBuild)
{
m_productsToBuild = productsToBuild;
}
@@ -183,11 +183,11 @@ void Executor::doBuild()
}
doSanityChecks();
- BuildProject * const project = m_productsToBuild.first()->project;
- m_evalContext = project->evaluationContext();
+ ProjectBuildData * const project = m_productsToBuild.first()->project->buildData.data();
+ m_evalContext = project->evaluationContext;
if (!m_evalContext) { // Is null before the first build.
m_evalContext = RulesEvaluationContextPtr(new RulesEvaluationContext(m_logger));
- project->setEvaluationContext(m_evalContext);
+ project->evaluationContext = m_evalContext;
}
m_logger.qbsDebug() << QString::fromLocal8Bit("[EXEC] preparing executor for %1 jobs "
@@ -216,13 +216,13 @@ void Executor::doBuild()
changedArtifacts.end());
// prepare products
- foreach (BuildProductPtr product, m_productsToBuild)
- product->rProduct->setupBuildEnvironment(m_evalContext->engine(), m_baseEnvironment);
+ foreach (ResolvedProductPtr product, m_productsToBuild)
+ product->setupBuildEnvironment(m_evalContext->engine(), m_baseEnvironment);
// find the root nodes
m_roots.clear();
- foreach (BuildProductPtr product, m_productsToBuild) {
- foreach (Artifact *targetArtifact, product->targetArtifacts) {
+ foreach (const ResolvedProductPtr &product, m_productsToBuild) {
+ foreach (Artifact *targetArtifact, product->buildData->targetArtifacts) {
m_roots += targetArtifact;
// The user expects that he can delete target artifacts and they get rebuilt.
@@ -582,8 +582,7 @@ void Executor::insertLeavesAfterAddingDependencies_recurse(Artifact *const artif
QString Executor::configString() const
{
- return tr(" for configuration %1")
- .arg(m_productsToBuild.first()->project->resolvedProject()->id());
+ return tr(" for configuration %1").arg(m_productsToBuild.first()->project->id());
}
void Executor::insertLeavesAfterAddingDependencies(QVector<Artifact *> dependencies)
@@ -608,10 +607,10 @@ void Executor::setupProgressObserver(bool mocWillRun)
return;
MocEffortCalculator mocEffortCalculator;
BuildEffortCalculator buildEffortCalculator;
- foreach (const BuildProductConstPtr &product, m_productsToBuild)
+ foreach (const ResolvedProductConstPtr &product, m_productsToBuild)
buildEffortCalculator.visitProduct(product);
if (mocWillRun) {
- foreach (const BuildProductConstPtr &product, m_productsToBuild)
+ foreach (const ResolvedProductConstPtr &product, m_productsToBuild)
mocEffortCalculator.visitProduct(product);
}
m_mocEffort = mocEffortCalculator.effort();
@@ -622,8 +621,10 @@ void Executor::setupProgressObserver(bool mocWillRun)
void Executor::doSanityChecks()
{
QBS_CHECK(!m_productsToBuild.isEmpty());
- for (int i = 1; i < m_productsToBuild.count(); ++i)
- QBS_CHECK(m_productsToBuild.at(i)->project == m_productsToBuild.first()->project);
+ foreach (const ResolvedProductConstPtr &product, m_productsToBuild) {
+ QBS_CHECK(product->buildData);
+ QBS_CHECK(product->project == m_productsToBuild.first()->project);
+ }
}
void Executor::handleError(const Error &error)
@@ -655,11 +656,11 @@ void Executor::addExecutorJobs(int jobNumber)
void Executor::runAutoMoc()
{
bool autoMocApplied = false;
- foreach (const BuildProductPtr &product, m_productsToBuild) {
+ foreach (const ResolvedProductPtr &product, m_productsToBuild) {
if (m_progressObserver && m_progressObserver->canceled())
throw Error(Tr::tr("Build canceled due to user request."));
// HACK call the automoc thingy here only if we have use qt/core module
- foreach (const ResolvedModuleConstPtr &m, product->rProduct->modules) {
+ foreach (const ResolvedModuleConstPtr &m, product->modules) {
if (m->name == "qt/core") {
autoMocApplied = true;
m_autoMoc->apply(product);
@@ -668,7 +669,7 @@ void Executor::runAutoMoc()
}
}
if (autoMocApplied) {
- foreach (const BuildProductConstPtr &product, m_productsToBuild)
+ foreach (const ResolvedProductConstPtr &product, m_productsToBuild)
CycleDetector(m_logger).visitProduct(product);
}
if (m_progressObserver)
@@ -701,7 +702,7 @@ void Executor::onProcessSuccess()
QBS_CHECK(processedArtifact);
// Update the timestamps of the outputs of the transformer we just executed.
- processedArtifact->project->markDirty();
+ processedArtifact->project->buildData->isDirty = true;
foreach (Artifact *artifact, processedArtifact->transformer->outputs) {
if (artifact->alwaysUpdated)
artifact->timestamp = FileTime::currentTime();
@@ -720,10 +721,10 @@ void Executor::finish()
QBS_CHECK(m_state != ExecutorIdle);
QStringList unbuiltProductNames;
- foreach (BuildProductPtr buildProduct, m_productsToBuild) {
- foreach (Artifact *artifact, buildProduct->targetArtifacts) {
+ foreach (const ResolvedProductPtr &product, m_productsToBuild) {
+ foreach (Artifact *artifact, product->buildData->targetArtifacts) {
if (artifact->buildState != Artifact::Built) {
- unbuiltProductNames += buildProduct->rProduct->name;
+ unbuiltProductNames += product->name;
break;
}
}
@@ -750,8 +751,8 @@ void Executor::finish()
*/
void Executor::initializeArtifactsState()
{
- foreach (const BuildProductPtr &product, m_productsToBuild) {
- foreach (Artifact *artifact, product->artifacts) {
+ foreach (const ResolvedProductPtr &product, m_productsToBuild) {
+ foreach (Artifact *artifact, product->buildData->artifacts) {
artifact->buildState = Artifact::Untouched;
artifact->inputsScanned = false;
artifact->timestampRetrieved = false;
diff --git a/src/lib/buildgraph/executor.h b/src/lib/buildgraph/executor.h
index f96a1d2d4..ae2e78bd0 100644
--- a/src/lib/buildgraph/executor.h
+++ b/src/lib/buildgraph/executor.h
@@ -33,14 +33,14 @@
#include "forward_decls.h"
#include <buildgraph/artifact.h>
#include <buildgraph/scanresultcache.h>
+#include <language/forward_decls.h>
+
#include <logging/logger.h>
#include <tools/buildoptions.h>
#include <tools/error.h>
-#include <tools/settings.h>
#include <QObject>
#include <QProcessEnvironment>
-#include <QVariant>
namespace qbs {
class ProcessResult;
@@ -51,7 +51,6 @@ class ExecutorJob;
class FileTime;
class InputArtifactScannerContext;
class ProgressObserver;
-class ScanResultCache;
class Executor : public QObject
{
@@ -64,7 +63,7 @@ public:
Executor(const Logger &logger, QObject *parent = 0);
~Executor();
- void setProducts(const QList<BuildProductPtr> &productsToBuild);
+ void setProducts(const QList<ResolvedProductPtr> &productsToBuild);
void setBuildOptions(const BuildOptions &buildOptions);
void setProgressObserver(ProgressObserver *observer) { m_progressObserver = observer; }
void setBaseEnvironment(const QProcessEnvironment &env) { m_baseEnvironment = env; }
@@ -124,7 +123,7 @@ private:
QList<ExecutorJob*> m_availableJobs;
QHash<ExecutorJob*, Artifact *> m_processingJobs;
ExecutorState m_state;
- QList<BuildProductPtr> m_productsToBuild;
+ QList<ResolvedProductPtr> m_productsToBuild;
QList<Artifact *> m_roots;
QList<Artifact *> m_leaves;
ScanResultCache m_scanResultCache;
diff --git a/src/lib/buildgraph/executorjob.cpp b/src/lib/buildgraph/executorjob.cpp
index 50059ee91..dd785105b 100644
--- a/src/lib/buildgraph/executorjob.cpp
+++ b/src/lib/buildgraph/executorjob.cpp
@@ -29,7 +29,6 @@
#include "executorjob.h"
-#include "buildproduct.h"
#include "command.h"
#include "jscommandexecutor.h"
#include "processcommandexecutor.h"
@@ -79,7 +78,7 @@ void ExecutorJob::setDryRun(bool enabled)
m_jsCommandExecutor->setDryRunEnabled(enabled);
}
-void ExecutorJob::run(Transformer *t, const BuildProduct *buildProduct)
+void ExecutorJob::run(Transformer *t, const ResolvedProductPtr &product)
{
QBS_ASSERT(m_currentCommandIdx == -1, return);
@@ -88,7 +87,7 @@ void ExecutorJob::run(Transformer *t, const BuildProduct *buildProduct)
return;
}
- m_processCommandExecutor->setProcessEnvironment(buildProduct->rProduct->buildEnvironment);
+ m_processCommandExecutor->setProcessEnvironment(product->buildEnvironment);
m_transformer = t;
runNextCommand();
diff --git a/src/lib/buildgraph/executorjob.h b/src/lib/buildgraph/executorjob.h
index 89a268466..358271e36 100644
--- a/src/lib/buildgraph/executorjob.h
+++ b/src/lib/buildgraph/executorjob.h
@@ -30,6 +30,7 @@
#ifndef QBS_EXECUTORJOB_H
#define QBS_EXECUTORJOB_H
+#include <language/forward_decls.h>
#include <tools/error.h>
#include <QObject>
@@ -44,7 +45,7 @@ class ProcessResult;
namespace Internal {
class AbstractCommandExecutor;
-class BuildProduct;
+class ProductBuildData;
class JsCommandExecutor;
class Logger;
class ProcessCommandExecutor;
@@ -59,7 +60,7 @@ public:
void setMainThreadScriptEngine(QScriptEngine *engine);
void setDryRun(bool enabled);
- void run(Transformer *t, const BuildProduct *buildProduct);
+ void run(Transformer *t, const ResolvedProductPtr &product);
void cancel();
void waitForFinished();
diff --git a/src/lib/buildgraph/forward_decls.h b/src/lib/buildgraph/forward_decls.h
index 27d8c1cce..d82885a2f 100644
--- a/src/lib/buildgraph/forward_decls.h
+++ b/src/lib/buildgraph/forward_decls.h
@@ -35,14 +35,8 @@ namespace qbs {
namespace Internal {
class Artifact;
-
-class BuildProject;
-typedef QSharedPointer<BuildProject> BuildProjectPtr;
-typedef QSharedPointer<const BuildProject> BuildProjectConstPtr;
-
-class BuildProduct;
-typedef QSharedPointer<BuildProduct> BuildProductPtr;
-typedef QSharedPointer<const BuildProduct> BuildProductConstPtr;
+class ProjectBuildData;
+class ProductBuildData;
class Transformer;
typedef QSharedPointer<Transformer> TransformerPtr;
diff --git a/src/lib/buildgraph/inputartifactscanner.cpp b/src/lib/buildgraph/inputartifactscanner.cpp
index 4085e5a58..34953dc5b 100644
--- a/src/lib/buildgraph/inputartifactscanner.cpp
+++ b/src/lib/buildgraph/inputartifactscanner.cpp
@@ -31,9 +31,8 @@
#include "artifact.h"
#include "buildgraph.h"
-#include "buildproduct.h"
-#include "buildproject.h"
-#include "rulesevaluationcontext.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
#include "transformer.h"
#include <language/language.h>
@@ -81,21 +80,23 @@ static QStringList collectIncludePaths(const QVariantMap &modules)
return QStringList(collectedPaths.toList());
}
-static ResolvedDependency resolveWithIncludePath(const QString &includePath, const ScanResultCache::Dependency &dependency, const BuildProduct *buildProduct)
+static ResolvedDependency resolveWithIncludePath(const QString &includePath,
+ const ScanResultCache::Dependency &dependency, const ResolvedProduct *product)
{
QString absDirPath = dependency.dirPath().isEmpty() ? includePath : FileInfo::resolvePath(includePath, dependency.dirPath());
if (!dependency.isClean())
absDirPath = QDir::cleanPath(absDirPath);
ResolvedDependency result;
- BuildProject *project = buildProduct->project;
+ const ResolvedProject *project = product->project.data();
Artifact *fileDependencyArtifact = 0;
Artifact *dependencyInProduct = 0;
Artifact *dependencyInOtherProduct = 0;
- foreach (Artifact *artifact, project->lookupArtifacts(absDirPath, dependency.fileName())) {
+ foreach (Artifact *artifact,
+ project->buildData->lookupArtifacts(absDirPath, dependency.fileName())) {
if (artifact->artifactType == Artifact::FileDependency)
fileDependencyArtifact = artifact;
- else if (artifact->product == buildProduct)
+ else if (artifact->product == product)
dependencyInProduct = artifact;
else
dependencyInOtherProduct = artifact;
@@ -258,7 +259,8 @@ void InputArtifactScanner::resolveScanResultDependencies(const QStringList &incl
// try base directory of source file
if (baseDirOfInFilePath.isNull())
baseDirOfInFilePath = FileInfo::path(filePathToBeScanned);
- resolvedDependency = resolveWithIncludePath(baseDirOfInFilePath, dependency, inputArtifact->product);
+ resolvedDependency = resolveWithIncludePath(baseDirOfInFilePath, dependency,
+ inputArtifact->product.data());
if (resolvedDependency.isValid())
goto resolved;
}
@@ -275,7 +277,8 @@ void InputArtifactScanner::resolveScanResultDependencies(const QStringList &incl
// try include paths
foreach (const QString &includePath, includePaths) {
- resolvedDependency = resolveWithIncludePath(includePath, dependency, inputArtifact->product);
+ resolvedDependency = resolveWithIncludePath(includePath, dependency,
+ inputArtifact->product.data());
if (resolvedDependency.isValid()) {
cachedResolvedDependencyItem->valid = true;
cachedResolvedDependencyItem->resolvedDependency = resolvedDependency;
@@ -307,10 +310,10 @@ resolved:
void InputArtifactScanner::handleDependency(ResolvedDependency &dependency)
{
- BuildProduct *product = m_artifact->product;
+ const ResolvedProductPtr product = m_artifact->product;
bool insertIntoProduct = true;
QBS_CHECK(m_artifact->artifactType == Artifact::Generated);
- QBS_CHECK(m_artifact->product);
+ QBS_CHECK(product);
if (!dependency.artifact) {
// The dependency is an existing file but does not exist in the build graph.
@@ -322,7 +325,7 @@ void InputArtifactScanner::handleDependency(ResolvedDependency &dependency)
dependency.artifact->artifactType = Artifact::FileDependency;
dependency.artifact->properties = m_artifact->properties;
dependency.artifact->setFilePath(dependency.filePath);
- m_artifact->project->insertFileDependency(dependency.artifact);
+ m_artifact->project->buildData->insertFileDependency(dependency.artifact);
} else if (dependency.artifact->artifactType == Artifact::FileDependency) {
// The dependency exists in the project's list of file dependencies.
if (m_logger.traceEnabled()) {
@@ -338,10 +341,10 @@ void InputArtifactScanner::handleDependency(ResolvedDependency &dependency)
insertIntoProduct = false;
} else {
// The dependency is in some other product.
- BuildProduct *otherProduct = dependency.artifact->product;
+ ResolvedProduct * const otherProduct = dependency.artifact->product;
if (m_logger.traceEnabled()) {
m_logger.qbsTrace() << QString::fromLocal8Bit("[DEPSCAN] found in product '%1': '%2'")
- .arg(otherProduct->rProduct->name, dependency.filePath);
+ .arg(otherProduct->name, dependency.filePath);
}
insertIntoProduct = false;
}
@@ -354,8 +357,8 @@ void InputArtifactScanner::handleDependency(ResolvedDependency &dependency)
} else {
if (m_artifact->children.contains(dependency.artifact))
return;
- if (insertIntoProduct && !product->artifacts.contains(dependency.artifact))
- product->insertArtifact(dependency.artifact, m_logger);
+ if (insertIntoProduct && !product->buildData->artifacts.contains(dependency.artifact))
+ insertArtifact(product, dependency.artifact, m_logger);
safeConnect(m_artifact, dependency.artifact, m_logger);
m_newDependencyAdded = true;
}
diff --git a/src/lib/buildgraph/jscommandexecutor.cpp b/src/lib/buildgraph/jscommandexecutor.cpp
index 9eeb377ef..8d07424db 100644
--- a/src/lib/buildgraph/jscommandexecutor.cpp
+++ b/src/lib/buildgraph/jscommandexecutor.cpp
@@ -31,7 +31,6 @@
#include "artifact.h"
#include "buildgraph.h"
-#include "buildproduct.h"
#include "command.h"
#include "transformer.h"
@@ -89,10 +88,10 @@ public:
scriptEngine->globalObject().setProperty("_qbs_transformer_ptr", scriptEngine->toScriptValue(trafoPtrStr));
Artifact *someOutputArtifact = *transformer->outputs.begin();
- if (someOutputArtifact->product) {
- const ResolvedProductConstPtr product = someOutputArtifact->product->rProduct;
+ const ResolvedProductConstPtr product = someOutputArtifact->product.toStrongRef();
+ if (product) {
setupScriptEngineForProduct(scriptEngine, product, transformer->rule,
- scriptEngine->globalObject());
+ scriptEngine->globalObject());
}
transformer->setupInputs(scriptEngine, scriptEngine->globalObject());
transformer->setupOutputs(scriptEngine, scriptEngine->globalObject());
diff --git a/src/lib/buildgraph/processcommandexecutor.cpp b/src/lib/buildgraph/processcommandexecutor.cpp
index 122e1e993..501b97b03 100644
--- a/src/lib/buildgraph/processcommandexecutor.cpp
+++ b/src/lib/buildgraph/processcommandexecutor.cpp
@@ -30,7 +30,6 @@
#include "processcommandexecutor.h"
#include "artifact.h"
-#include "buildproduct.h"
#include "command.h"
#include "transformer.h"
@@ -276,7 +275,7 @@ void ProcessCommandExecutor::removeResponseFile()
QString ProcessCommandExecutor::findProcessCommandInPath()
{
Artifact * const outputNode = *transformer()->outputs.begin();
- ResolvedProduct * const product = outputNode->product->rProduct.data();
+ const ResolvedProductPtr product = outputNode->product;
const ProcessCommand * const cmd = processCommand();
QString fullProgramPath = product->executablePathCache.value(cmd->program());
if (!fullProgramPath.isEmpty())
@@ -309,7 +308,7 @@ QString ProcessCommandExecutor::findProcessCommandInPath()
QString ProcessCommandExecutor::findProcessCommandBySuffix()
{
Artifact * const outputNode = *transformer()->outputs.begin();
- ResolvedProduct * const product = outputNode->product->rProduct.data();
+ const ResolvedProductPtr product = outputNode->product;
const ProcessCommand * const cmd = processCommand();
QString fullProgramPath = product->executablePathCache.value(cmd->program());
if (!fullProgramPath.isEmpty())
diff --git a/src/lib/buildgraph/productbuilddata.cpp b/src/lib/buildgraph/productbuilddata.cpp
new file mode 100644
index 000000000..cc0596139
--- /dev/null
+++ b/src/lib/buildgraph/productbuilddata.cpp
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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 "productbuilddata.h"
+
+#include "artifact.h"
+#include "projectbuilddata.h"
+#include <language/language.h>
+#include <logging/logger.h>
+#include <tools/error.h>
+#include <tools/persistence.h>
+#include <tools/qbsassert.h>
+
+namespace qbs {
+namespace Internal {
+
+ProductBuildData::~ProductBuildData()
+{
+ qDeleteAll(artifacts);
+}
+
+void ProductBuildData::load(PersistentPool &pool)
+{
+ // artifacts
+ int i;
+ pool.stream() >> i;
+ artifacts.clear();
+ for (; --i >= 0;) {
+ Artifact *artifact = pool.idLoad<Artifact>();
+ artifacts.insert(artifact);
+ }
+
+ // edges
+ for (i = artifacts.count(); --i >= 0;) {
+ Artifact *artifact = pool.idLoad<Artifact>();
+ int k;
+ pool.stream() >> k;
+ artifact->parents.clear();
+ artifact->parents.reserve(k);
+ for (; --k >= 0;)
+ artifact->parents.insert(pool.idLoad<Artifact>());
+
+ pool.stream() >> k;
+ artifact->children.clear();
+ artifact->children.reserve(k);
+ for (; --k >= 0;)
+ artifact->children.insert(pool.idLoad<Artifact>());
+
+ pool.stream() >> k;
+ artifact->fileDependencies.clear();
+ artifact->fileDependencies.reserve(k);
+ for (; --k >= 0;)
+ artifact->fileDependencies.insert(pool.idLoad<Artifact>());
+ }
+
+ // other data
+ pool.loadContainer(targetArtifacts);
+}
+
+void ProductBuildData::store(PersistentPool &pool) const
+{
+ pool.stream() << artifacts.count();
+
+ //artifacts
+ for (ArtifactList::const_iterator i = artifacts.constBegin(); i != artifacts.constEnd(); ++i)
+ pool.store(*i);
+
+ // edges
+ for (ArtifactList::const_iterator i = artifacts.constBegin(); i != artifacts.constEnd(); ++i) {
+ Artifact * artifact = *i;
+ pool.store(artifact);
+
+ pool.stream() << artifact->parents.count();
+ foreach (Artifact * n, artifact->parents)
+ pool.store(n);
+ pool.stream() << artifact->children.count();
+ foreach (Artifact * n, artifact->children)
+ pool.store(n);
+ pool.stream() << artifact->fileDependencies.count();
+ foreach (Artifact * n, artifact->fileDependencies)
+ pool.store(n);
+ }
+
+ // other data
+ pool.storeContainer(targetArtifacts);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/buildgraph/buildproduct.h b/src/lib/buildgraph/productbuilddata.h
index ff9e59849..ac86f323f 100644
--- a/src/lib/buildgraph/buildproduct.h
+++ b/src/lib/buildgraph/productbuilddata.h
@@ -26,13 +26,11 @@
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
-#ifndef QBS_BUILDPRODUCT_H
-#define QBS_BUILDPRODUCT_H
+#ifndef QBS_PRODUCTBUILDDATA_H
+#define QBS_PRODUCTBUILDDATA_H
#include "artifactlist.h"
-#include "forward_decls.h"
#include <language/forward_decls.h>
-#include <tools/weakpointer.h>
#include <tools/persistentobject.h>
@@ -43,38 +41,20 @@ namespace qbs {
namespace Internal {
class Logger;
-class BuildProduct : public PersistentObject
+class ProductBuildData : public PersistentObject
{
public:
- static BuildProductPtr create() { return BuildProductPtr(new BuildProduct); }
+ ~ProductBuildData();
- ~BuildProduct();
-
- void dump() const;
- const QList<RuleConstPtr> &topSortedRules() const;
- Artifact *lookupArtifact(const QString &dirPath, const QString &fileName) const;
- Artifact *lookupArtifact(const QString &filePath) const;
- Artifact *lookupArtifact(const Artifact *artifact) const;
- Artifact *createArtifact(const SourceArtifactConstPtr &sourceArtifact, const Logger &logger);
- void insertArtifact(Artifact *n, const Logger &logger);
-
- WeakPointer<BuildProject> project;
- ResolvedProductPtr rProduct;
QSet<Artifact *> targetArtifacts;
- QList<BuildProductPtr> dependencies;
ArtifactList artifacts;
-
-private:
- BuildProduct();
+ QList<RuleConstPtr> topSortedRules;
void load(PersistentPool &pool);
void store(PersistentPool &pool) const;
-
-private:
- mutable QList<RuleConstPtr> m_topSortedRules;
};
} // namespace Internal
} // namespace qbs
-#endif // QBS_BUILDPRODUCT_H
+#endif // QBS_PRODUCTBUILDDATA_H
diff --git a/src/lib/buildgraph/productinstaller.cpp b/src/lib/buildgraph/productinstaller.cpp
index e66d2c69e..81d40f4c9 100644
--- a/src/lib/buildgraph/productinstaller.cpp
+++ b/src/lib/buildgraph/productinstaller.cpp
@@ -29,15 +29,14 @@
#include "productinstaller.h"
#include "artifact.h"
-#include "buildproduct.h"
-#include "buildproject.h"
-#include "rulesevaluationcontext.h"
+#include "productbuilddata.h"
#include <language/language.h>
#include <logging/translator.h>
#include <tools/qbsassert.h>
#include <tools/error.h>
#include <tools/fileinfo.h>
#include <tools/progressobserver.h>
+#include <tools/qbsassert.h>
#include <QDir>
#include <QFileInfo>
@@ -45,7 +44,7 @@
namespace qbs {
namespace Internal {
-ProductInstaller::ProductInstaller(const QList<BuildProductPtr> &products,
+ProductInstaller::ProductInstaller(const QList<ResolvedProductPtr> &products,
const InstallOptions &options, ProgressObserver *observer, const Logger &logger)
: m_products(products), m_options(options), m_observer(observer), m_logger(logger)
{
@@ -65,11 +64,11 @@ ProductInstaller::ProductInstaller(const QList<BuildProductPtr> &products,
if (m_products.isEmpty())
throw Error(Tr::tr("Cannot deduce install root, because there are no products."));
- const BuildProductConstPtr &product = m_products.first();
- m_options.installRoot = product->rProduct->properties
- ->qbsPropertyValue(QLatin1String("sysroot")).toString();
+ const ResolvedProductConstPtr &product = m_products.first();
+ m_options.installRoot
+ = product->properties->qbsPropertyValue(QLatin1String("sysroot")).toString();
if (m_options.installRoot.isEmpty()) {
- m_options.installRoot = product->project->resolvedProject()->buildDirectory
+ m_options.installRoot = product->project->buildDirectory
+ QLatin1Char('/') + InstallOptions::defaultInstallRoot();
} else if (m_options.removeFirst) {
throw Error(Tr::tr("Refusing to remove sysroot."));
@@ -82,8 +81,9 @@ void ProductInstaller::install()
removeInstallRoot();
QList<const Artifact *> artifactsToInstall;
- foreach (const BuildProductConstPtr &product, m_products) {
- foreach (const Artifact *artifact, product->artifacts) {
+ foreach (const ResolvedProductConstPtr &product, m_products) {
+ QBS_CHECK(product->buildData);
+ foreach (const Artifact *artifact, product->buildData->artifacts) {
if (artifact->properties->qbsPropertyValue(QLatin1String("install")).toBool())
artifactsToInstall += artifact;
}
diff --git a/src/lib/buildgraph/productinstaller.h b/src/lib/buildgraph/productinstaller.h
index 853f04e26..acea496f1 100644
--- a/src/lib/buildgraph/productinstaller.h
+++ b/src/lib/buildgraph/productinstaller.h
@@ -30,6 +30,8 @@
#define QBS_PRODUCT_INSTALLER_H
#include "forward_decls.h"
+
+#include <language/forward_decls.h>
#include <logging/logger.h>
#include <tools/installoptions.h>
@@ -42,7 +44,7 @@ class ProgressObserver;
class ProductInstaller
{
public:
- ProductInstaller(const QList<BuildProductPtr> &products, const InstallOptions &options,
+ ProductInstaller(const QList<ResolvedProductPtr> &products, const InstallOptions &options,
ProgressObserver *observer, const Logger &logger);
void install();
@@ -51,7 +53,7 @@ private:
void copyFile(const Artifact *artifact);
void handleError(const QString &message);
- const QList<BuildProductPtr> m_products;
+ const QList<ResolvedProductPtr> m_products;
InstallOptions m_options;
ProgressObserver * const m_observer;
Logger m_logger;
diff --git a/src/lib/buildgraph/projectbuilddata.cpp b/src/lib/buildgraph/projectbuilddata.cpp
new file mode 100644
index 000000000..d3bd7b510
--- /dev/null
+++ b/src/lib/buildgraph/projectbuilddata.cpp
@@ -0,0 +1,380 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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 "projectbuilddata.h"
+
+#include "artifact.h"
+#include "buildgraph.h"
+#include "productbuilddata.h"
+#include "command.h"
+#include "cycledetector.h"
+#include "rulesapplicator.h"
+#include "rulesevaluationcontext.h"
+#include "transformer.h"
+#include <language/language.h>
+#include <language/scriptengine.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/persistence.h>
+#include <tools/qbsassert.h>
+
+namespace qbs {
+namespace Internal {
+
+ProjectBuildData::ProjectBuildData() : isDirty(false)
+{
+}
+
+ProjectBuildData::~ProjectBuildData()
+{
+ qDeleteAll(dependencyArtifacts);
+}
+
+QString ProjectBuildData::deriveBuildGraphFilePath(const QString &buildDir, const QString &projectId)
+{
+ return buildDir + QLatin1Char('/') + projectId + QLatin1String(".bg");
+}
+
+void ProjectBuildData::insertIntoArtifactLookupTable(Artifact *artifact)
+{
+ QList<Artifact *> &lst = m_artifactLookupTable[artifact->fileName()][artifact->dirPath()];
+ if (!lst.contains(artifact))
+ lst.append(artifact);
+}
+
+void ProjectBuildData::removeFromArtifactLookupTable(Artifact *artifact)
+{
+ m_artifactLookupTable[artifact->fileName()][artifact->dirPath()].removeOne(artifact);
+}
+
+QList<Artifact *> ProjectBuildData::lookupArtifacts(const QString &filePath) const
+{
+ QString dirPath, fileName;
+ FileInfo::splitIntoDirectoryAndFileName(filePath, &dirPath, &fileName);
+ return lookupArtifacts(dirPath, fileName);
+}
+
+QList<Artifact *> ProjectBuildData::lookupArtifacts(const QString &dirPath, const QString &fileName) const
+{
+ QList<Artifact *> result = m_artifactLookupTable.value(fileName).value(dirPath);
+ return result;
+}
+
+QList<Artifact *> ProjectBuildData::lookupArtifacts(const Artifact *artifact) const
+{
+ return lookupArtifacts(artifact->dirPath(), artifact->fileName());
+}
+
+void ProjectBuildData::insertFileDependency(Artifact *artifact)
+{
+ QBS_CHECK(artifact->artifactType == Artifact::FileDependency);
+ dependencyArtifacts += artifact;
+ insertIntoArtifactLookupTable(artifact);
+}
+
+static bool commandsEqual(const TransformerConstPtr &t1, const TransformerConstPtr &t2)
+{
+ if (t1->commands.count() != t2->commands.count())
+ return false;
+ for (int i = 0; i < t1->commands.count(); ++i)
+ if (!t1->commands.at(i)->equals(t2->commands.at(i)))
+ return false;
+ return true;
+}
+
+
+void ProjectBuildData::removeArtifact(Artifact *artifact, const Logger &logger)
+{
+ if (logger.traceEnabled())
+ logger.qbsTrace() << "[BG] remove artifact " << relativeArtifactFileName(artifact);
+
+ removeGeneratedArtifactFromDisk(artifact, logger);
+ artifact->product->buildData->artifacts.remove(artifact);
+ removeFromArtifactLookupTable(artifact);
+ artifact->product->buildData->targetArtifacts.remove(artifact);
+ foreach (Artifact *parent, artifact->parents) {
+ parent->children.remove(artifact);
+ if (parent->transformer) {
+ parent->transformer->inputs.remove(artifact);
+ artifactsThatMustGetNewTransformers += parent;
+ }
+ }
+ foreach (Artifact *child, artifact->children)
+ child->parents.remove(artifact);
+ artifact->children.clear();
+ artifact->parents.clear();
+ isDirty = true;
+}
+
+void ProjectBuildData::updateNodesThatMustGetNewTransformer(const Logger &logger)
+{
+ RulesEvaluationContext::Scope s(evaluationContext.data());
+ foreach (Artifact *artifact, artifactsThatMustGetNewTransformers)
+ updateNodeThatMustGetNewTransformer(artifact, logger);
+ artifactsThatMustGetNewTransformers.clear();
+}
+
+void ProjectBuildData::updateNodeThatMustGetNewTransformer(Artifact *artifact, const Logger &logger)
+{
+ QBS_CHECK(artifact->transformer);
+
+ if (logger.debugEnabled()) {
+ logger.qbsDebug() << "[BG] updating transformer for "
+ << relativeArtifactFileName(artifact);
+ }
+
+ removeGeneratedArtifactFromDisk(artifact, logger);
+
+ const RuleConstPtr rule = artifact->transformer->rule;
+ isDirty = true;
+ artifact->transformer = TransformerPtr();
+
+ ArtifactsPerFileTagMap artifactsPerFileTag;
+ foreach (Artifact *input, artifact->children) {
+ foreach (const FileTag &fileTag, input->fileTags)
+ artifactsPerFileTag[fileTag] += input;
+ }
+ RulesApplicator rulesApplier(artifact->product, artifactsPerFileTag, logger);
+ rulesApplier.applyRule(rule);
+}
+
+void ProjectBuildData::load(PersistentPool &pool)
+{
+ int count;
+ pool.stream() >> count;
+ dependencyArtifacts.clear();
+ dependencyArtifacts.reserve(count);
+ for (; --count >= 0;) {
+ Artifact *artifact = pool.idLoad<Artifact>();
+ insertFileDependency(artifact);
+ }
+}
+
+void ProjectBuildData::store(PersistentPool &pool) const
+{
+ pool.storeContainer(dependencyArtifacts);
+}
+
+
+BuildDataResolver::BuildDataResolver(const Logger &logger) : m_logger(logger)
+{
+}
+
+void BuildDataResolver::resolveBuildData(const ResolvedProjectPtr &resolvedProject,
+ const RulesEvaluationContextPtr &evalContext)
+{
+ QBS_CHECK(!resolvedProject->buildData);
+ m_project = resolvedProject;
+ resolvedProject->buildData.reset(new ProjectBuildData);
+ resolvedProject->buildData->evaluationContext = evalContext;
+ evalContext->initializeObserver(Tr::tr("Setting up build graph for configuration %1")
+ .arg(resolvedProject->id()), resolvedProject->products.count());
+ foreach (ResolvedProductPtr rProduct, resolvedProject->products) {
+ if (rProduct->enabled)
+ resolveProductBuildData(rProduct);
+ evalContext->incrementProgressValue();
+ }
+ CycleDetector(m_logger).visitProject(m_project);
+}
+
+void BuildDataResolver::resolveProductBuildDataForExistingProject(const ResolvedProjectPtr &project,
+ const QList<ResolvedProductPtr> &freshProducts)
+{
+ m_project = project;
+ foreach (const ResolvedProductPtr &product, freshProducts)
+ resolveProductBuildData(product);
+}
+
+/**
+ * Rescues the following data from the source to target:
+ * - dependencies between artifacts,
+ * - time stamps of artifacts, if their commands have not changed.
+ */
+void BuildDataResolver::rescueBuildData(const ResolvedProjectConstPtr &source,
+ const ResolvedProjectPtr &target, Logger logger)
+{
+ QHash<QString, ResolvedProductConstPtr> sourceProductsByName;
+ foreach (const ResolvedProductConstPtr &product, source->products)
+ sourceProductsByName.insert(product->name, product);
+
+ foreach (const ResolvedProductPtr &product, target->products) {
+ ResolvedProductConstPtr sourceProduct = sourceProductsByName.value(product->name);
+ if (!sourceProduct)
+ continue;
+
+ if (!product->enabled || !sourceProduct->enabled)
+ continue;
+ QBS_CHECK(product->buildData);
+
+ if (logger.traceEnabled()) {
+ logger.qbsTrace() << QString::fromLocal8Bit("[BG] rescue data of "
+ "product '%1'").arg(product->name);
+ }
+
+ foreach (Artifact *artifact, product->buildData->artifacts) {
+ if (logger.traceEnabled()) {
+ logger.qbsTrace() << QString::fromLocal8Bit("[BG] artifact '%1'")
+ .arg(artifact->fileName());
+ }
+
+ Artifact *otherArtifact = lookupArtifact(sourceProduct, artifact);
+ if (!otherArtifact || !otherArtifact->transformer) {
+ if (logger.traceEnabled())
+ logger.qbsTrace() << QString::fromLocal8Bit("[BG] no transformer data");
+ continue;
+ }
+
+ if (artifact->transformer
+ && !commandsEqual(artifact->transformer, otherArtifact->transformer)) {
+ if (logger.traceEnabled())
+ logger.qbsTrace() << QString::fromLocal8Bit("[BG] artifact invalidated");
+ continue;
+ }
+ artifact->timestamp = otherArtifact->timestamp;
+
+ foreach (Artifact *otherChild, otherArtifact->children) {
+ // skip transform edges
+ if (otherArtifact->transformer->inputs.contains(otherChild))
+ continue;
+
+ QList<Artifact *> children = target->buildData->lookupArtifacts(otherChild);
+ foreach (Artifact *child, children) {
+ if (!artifact->children.contains(child))
+ safeConnect(artifact, child, logger);
+ }
+ }
+ }
+ }
+}
+
+void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &product)
+{
+ if (product->buildData)
+ return;
+
+ evalContext()->checkForCancelation();
+
+ product->buildData.reset(new ProductBuildData);
+ ArtifactsPerFileTagMap artifactsPerFileTag;
+
+ foreach (ResolvedProductPtr dependency, product->dependencies) {
+ if (!dependency->enabled) {
+ QString msg = Tr::tr("Product '%1' depends on '%2' but '%2' is disabled.");
+ throw Error(msg.arg(product->name, dependency->name));
+ }
+ resolveProductBuildData(dependency);
+ }
+
+ //add qbsFile artifact
+ Artifact *qbsFileArtifact = lookupArtifact(product, product->location.fileName);
+ if (!qbsFileArtifact) {
+ qbsFileArtifact = new Artifact(m_project);
+ qbsFileArtifact->artifactType = Artifact::SourceFile;
+ qbsFileArtifact->setFilePath(product->location.fileName);
+ qbsFileArtifact->properties = product->properties;
+ insertArtifact(product, qbsFileArtifact, m_logger);
+ }
+ qbsFileArtifact->fileTags.insert("qbs");
+ artifactsPerFileTag["qbs"].insert(qbsFileArtifact);
+
+ // read sources
+ foreach (const SourceArtifactConstPtr &sourceArtifact, product->allEnabledFiles()) {
+ QString filePath = sourceArtifact->absoluteFilePath;
+ if (lookupArtifact(product, filePath))
+ continue; // ignore duplicate artifacts
+
+ Artifact *artifact = createArtifact(product, sourceArtifact, m_logger);
+ foreach (const FileTag &fileTag, artifact->fileTags)
+ artifactsPerFileTag[fileTag].insert(artifact);
+ }
+
+ // read manually added transformers
+ foreach (const ResolvedTransformer::Ptr rtrafo, product->transformers) {
+ ArtifactList inputArtifacts;
+ foreach (const QString &inputFileName, rtrafo->inputs) {
+ Artifact *artifact = lookupArtifact(product, inputFileName);
+ if (!artifact)
+ throw Error(QString("Can't find artifact '%0' in the list of source files.").arg(inputFileName));
+ inputArtifacts += artifact;
+ }
+ TransformerPtr transformer = Transformer::create();
+ transformer->inputs = inputArtifacts;
+ const RulePtr rule = Rule::create();
+ rule->jsImports = rtrafo->jsImports;
+ ResolvedModulePtr module = ResolvedModule::create();
+ module->name = rtrafo->module->name;
+ rule->module = module;
+ rule->script = rtrafo->transform;
+ foreach (const SourceArtifactConstPtr &sourceArtifact, rtrafo->outputs) {
+ Artifact *outputArtifact = createArtifact(product, sourceArtifact, m_logger);
+ outputArtifact->artifactType = Artifact::Generated;
+ outputArtifact->transformer = transformer;
+ transformer->outputs += outputArtifact;
+ product->buildData->targetArtifacts += outputArtifact;
+ foreach (Artifact *inputArtifact, inputArtifacts)
+ safeConnect(outputArtifact, inputArtifact, m_logger);
+ foreach (const FileTag &fileTag, outputArtifact->fileTags)
+ artifactsPerFileTag[fileTag].insert(outputArtifact);
+
+ RuleArtifactPtr ruleArtifact = RuleArtifact::create();
+ ruleArtifact->fileName = outputArtifact->filePath();
+ ruleArtifact->fileTags = outputArtifact->fileTags;
+ rule->artifacts += ruleArtifact;
+ }
+ transformer->rule = rule;
+
+ RulesEvaluationContext::Scope s(evalContext().data());
+ setupScriptEngineForProduct(engine(), product, transformer->rule, scope());
+ transformer->setupInputs(engine(), scope());
+ transformer->setupOutputs(engine(), scope());
+ transformer->createCommands(rtrafo->transform, evalContext());
+ if (transformer->commands.isEmpty())
+ throw Error(QString("There's a transformer without commands."), rtrafo->transform->location);
+ }
+
+ RulesApplicator(product, artifactsPerFileTag, m_logger).applyAllRules();
+ addTargetArtifacts(product, artifactsPerFileTag, m_logger);
+}
+
+RulesEvaluationContextPtr BuildDataResolver::evalContext() const
+{
+ return m_project->buildData->evaluationContext;
+}
+
+ScriptEngine *BuildDataResolver::engine() const
+{
+ return evalContext()->engine();
+}
+
+QScriptValue BuildDataResolver::scope() const
+{
+ return evalContext()->scope();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/buildgraph/projectbuilddata.h b/src/lib/buildgraph/projectbuilddata.h
new file mode 100644
index 000000000..ee656af7e
--- /dev/null
+++ b/src/lib/buildgraph/projectbuilddata.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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_PROJECTBUILDDATA_H
+#define QBS_PROJECTBUILDDATA_H
+
+#include "artifactlist.h"
+#include "forward_decls.h"
+#include <language/forward_decls.h>
+#include <logging/logger.h>
+#include <tools/persistentobject.h>
+
+#include <QHash>
+#include <QList>
+#include <QScriptValue>
+#include <QSet>
+#include <QString>
+
+namespace qbs {
+namespace Internal {
+class ScriptEngine;
+
+class ProjectBuildData : public PersistentObject
+{
+public:
+ ProjectBuildData();
+ ~ProjectBuildData();
+
+ static QString deriveBuildGraphFilePath(const QString &buildDir, const QString &projectId);
+
+ void insertIntoArtifactLookupTable(Artifact *artifact);
+ void removeFromArtifactLookupTable(Artifact *artifact);
+ QList<Artifact *> lookupArtifacts(const QString &filePath) const;
+ QList<Artifact *> lookupArtifacts(const QString &dirPath, const QString &fileName) const;
+ QList<Artifact *> lookupArtifacts(const Artifact *artifact) const;
+ void insertFileDependency(Artifact *artifact);
+ void removeArtifact(Artifact *artifact, const Logger &logger);
+ void updateNodesThatMustGetNewTransformer(const Logger &logger);
+
+ ArtifactList dependencyArtifacts;
+ RulesEvaluationContextPtr evaluationContext;
+ QSet<Artifact *> artifactsThatMustGetNewTransformers;
+ bool isDirty;
+
+private:
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+ void updateNodeThatMustGetNewTransformer(Artifact *artifact, const Logger &logger);
+
+private:
+ QHash<QString, QHash<QString, QList<Artifact *> > > m_artifactLookupTable;
+};
+
+
+class BuildDataResolver
+{
+public:
+ BuildDataResolver(const Logger &logger);
+ void resolveBuildData(const ResolvedProjectPtr &resolvedProject,
+ const RulesEvaluationContextPtr &evalContext);
+ void resolveProductBuildDataForExistingProject(const ResolvedProjectPtr &project,
+ const QList<ResolvedProductPtr> &freshProducts);
+
+ static void rescueBuildData(const ResolvedProjectConstPtr &source,
+ const ResolvedProjectPtr &target, Logger logger);
+
+private:
+ void resolveProductBuildData(const ResolvedProductPtr &product);
+ RulesEvaluationContextPtr evalContext() const;
+ ScriptEngine *engine() const;
+ QScriptValue scope() const;
+
+ ResolvedProjectPtr m_project;
+ Logger m_logger;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_PROJECTBUILDDATA_H
diff --git a/src/lib/buildgraph/rulesapplicator.cpp b/src/lib/buildgraph/rulesapplicator.cpp
index 8ac853573..16e9a8d5a 100644
--- a/src/lib/buildgraph/rulesapplicator.cpp
+++ b/src/lib/buildgraph/rulesapplicator.cpp
@@ -30,8 +30,8 @@
#include "artifact.h"
#include "buildgraph.h"
-#include "buildproduct.h"
-#include "buildproject.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
#include "rulesevaluationcontext.h"
#include "transformer.h"
#include <jsextensions/moduleproperties.h>
@@ -48,9 +48,9 @@
namespace qbs {
namespace Internal {
-RulesApplicator::RulesApplicator(BuildProduct *product, ArtifactsPerFileTagMap &artifactsPerFileTag,
- const Logger &logger)
- : m_buildProduct(product)
+RulesApplicator::RulesApplicator(const ResolvedProductPtr &product,
+ ArtifactsPerFileTagMap &artifactsPerFileTag, const Logger &logger)
+ : m_product(product)
, m_artifactsPerFileTag(artifactsPerFileTag)
, m_logger(logger)
, m_productObjectId(-1)
@@ -59,8 +59,8 @@ RulesApplicator::RulesApplicator(BuildProduct *product, ArtifactsPerFileTagMap &
void RulesApplicator::applyAllRules()
{
- RulesEvaluationContext::Scope s(m_buildProduct->project->evaluationContext().data());
- foreach (const RuleConstPtr &rule, m_buildProduct->topSortedRules())
+ RulesEvaluationContext::Scope s(m_product->project->buildData->evaluationContext.data());
+ foreach (const RuleConstPtr &rule, m_product->topSortedRules())
applyRule(rule);
}
@@ -68,7 +68,7 @@ void RulesApplicator::applyRule(const RuleConstPtr &rule)
{
m_rule = rule;
QScriptValue scopeValue = scope();
- setupScriptEngineForProduct(engine(), m_buildProduct->rProduct, m_rule, scopeValue, this);
+ setupScriptEngineForProduct(engine(), m_product, m_rule, scopeValue, this);
Q_ASSERT_X(scope().property("product").strictlyEquals(engine()->evaluate("product")),
"BG", "Product object is not in current scope.");
m_productObjectId = scopeValue.property(QLatin1String("product")).objectId();
@@ -105,9 +105,10 @@ void RulesApplicator::doApply(const ArtifactList &inputArtifacts)
ArtifactList usingArtifacts;
if (!m_rule->usings.isEmpty()) {
const FileTags usingsFileTags = m_rule->usings;
- foreach (const BuildProductPtr &dep, m_buildProduct->dependencies) {
+ foreach (const ResolvedProductPtr &dep, m_product->dependencies) {
+ QBS_CHECK(dep->buildData);
ArtifactList artifactsToCheck;
- foreach (Artifact *targetArtifact, dep->targetArtifacts)
+ foreach (Artifact *targetArtifact, dep->buildData->targetArtifacts)
artifactsToCheck.unite(targetArtifact->transformer->outputs);
foreach (Artifact *artifact, artifactsToCheck) {
FileTags matchingFileTags = artifact->fileTags;
@@ -146,7 +147,7 @@ void RulesApplicator::doApply(const ArtifactList &inputArtifacts)
}
m_transformer->outputs.insert(outputArtifact);
- m_buildProduct->project->removeFromArtifactsThatMustGetNewTransformers(outputArtifact);
+ m_product->project->buildData->artifactsThatMustGetNewTransformers -= outputArtifact;
}
m_transformer->setupInputs(engine(), scope());
@@ -195,10 +196,10 @@ void RulesApplicator::setupScriptEngineForArtifact(Artifact *artifact)
QString basedir;
if (artifact->artifactType == Artifact::SourceFile) {
- QDir sourceDir(m_buildProduct->rProduct->sourceDirectory);
+ QDir sourceDir(m_product->sourceDirectory);
basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->filePath()));
} else {
- QDir buildDir(m_buildProduct->project->resolvedProject()->buildDirectory);
+ QDir buildDir(m_product->project->buildDirectory);
basedir = FileInfo::path(buildDir.relativeFilePath(artifact->filePath()));
}
@@ -225,7 +226,7 @@ Artifact *RulesApplicator::createOutputArtifact(const RuleArtifactConstPtr &rule
outputPath.replace("..", "dotdot"); // don't let the output artifact "escape" its build dir
outputPath = resolveOutPath(outputPath);
- Artifact *outputArtifact = m_buildProduct->lookupArtifact(outputPath);
+ Artifact *outputArtifact = lookupArtifact(m_product, outputPath);
if (outputArtifact) {
if (outputArtifact->transformer && outputArtifact->transformer != m_transformer) {
// This can happen when applying rules after scanning for additional file tags.
@@ -259,24 +260,24 @@ Artifact *RulesApplicator::createOutputArtifact(const RuleArtifactConstPtr &rule
}
outputArtifact->fileTags += ruleArtifact->fileTags;
} else {
- outputArtifact = new Artifact(m_buildProduct->project);
+ outputArtifact = new Artifact(m_product->project);
outputArtifact->artifactType = Artifact::Generated;
outputArtifact->setFilePath(outputPath);
outputArtifact->fileTags = ruleArtifact->fileTags;
outputArtifact->alwaysUpdated = ruleArtifact->alwaysUpdated;
- m_buildProduct->insertArtifact(outputArtifact, m_logger);
+ insertArtifact(m_product, outputArtifact, m_logger);
}
if (outputArtifact->fileTags.isEmpty())
- outputArtifact->fileTags = m_buildProduct->rProduct->fileTagsForFileName(outputArtifact->fileName());
+ outputArtifact->fileTags = m_product->fileTagsForFileName(outputArtifact->fileName());
if (m_rule->multiplex)
- outputArtifact->properties = m_buildProduct->rProduct->properties;
+ outputArtifact->properties = m_product->properties;
else
outputArtifact->properties= (*inputArtifacts.constBegin())->properties;
- for (int i = 0; i < m_buildProduct->rProduct->artifactProperties.count(); ++i) {
- const ArtifactPropertiesConstPtr &props = m_buildProduct->rProduct->artifactProperties.at(i);
+ for (int i = 0; i < m_product->artifactProperties.count(); ++i) {
+ const ArtifactPropertiesConstPtr &props = m_product->artifactProperties.at(i);
FileTags filter = props->fileTagsFilter();
if (!filter.intersect(outputArtifact->fileTags).isEmpty()) {
outputArtifact->properties = props->propertyMap();
@@ -302,7 +303,7 @@ Artifact *RulesApplicator::createOutputArtifact(const RuleArtifactConstPtr &rule
QString RulesApplicator::resolveOutPath(const QString &path) const
{
- QString buildDir = m_buildProduct->project->resolvedProject()->buildDirectory;
+ QString buildDir = m_product->project->buildDirectory;
QString result = FileInfo::resolvePath(buildDir, path);
result = QDir::cleanPath(result);
return result;
@@ -310,7 +311,7 @@ QString RulesApplicator::resolveOutPath(const QString &path) const
RulesEvaluationContextPtr RulesApplicator::evalContext() const
{
- return m_buildProduct->project->evaluationContext();
+ return m_product->project->buildData->evaluationContext;
}
ScriptEngine *RulesApplicator::engine() const
diff --git a/src/lib/buildgraph/rulesapplicator.h b/src/lib/buildgraph/rulesapplicator.h
index 7842d266d..9d7d75ff7 100644
--- a/src/lib/buildgraph/rulesapplicator.h
+++ b/src/lib/buildgraph/rulesapplicator.h
@@ -49,7 +49,7 @@ typedef QMap<FileTag, ArtifactList> ArtifactsPerFileTagMap;
class RulesApplicator : private ScriptPropertyObserver
{
public:
- RulesApplicator(BuildProduct *product, ArtifactsPerFileTagMap &artifactsPerFileTag,
+ RulesApplicator(const ResolvedProductPtr &product, ArtifactsPerFileTagMap &artifactsPerFileTag,
const Logger &logger);
void applyAllRules();
void applyRule(const RuleConstPtr &rule);
@@ -65,7 +65,7 @@ private:
QScriptValue scope() const;
void onPropertyRead(const QScriptValue &object, const QString &name, const QScriptValue &value);
- BuildProduct * const m_buildProduct;
+ const ResolvedProductPtr m_product;
ArtifactsPerFileTagMap &m_artifactsPerFileTag;
RuleConstPtr m_rule;
diff --git a/src/lib/buildgraph/timestampsupdater.cpp b/src/lib/buildgraph/timestampsupdater.cpp
index eeff6e071..ec99b94cb 100644
--- a/src/lib/buildgraph/timestampsupdater.cpp
+++ b/src/lib/buildgraph/timestampsupdater.cpp
@@ -30,11 +30,11 @@
#include "artifact.h"
#include "artifactvisitor.h"
-#include "buildproduct.h"
-#include "buildproject.h"
-#include "rulesevaluationcontext.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
#include <language/language.h>
#include <tools/filetime.h>
+#include <tools/qbsassert.h>
#include <QFile>
@@ -47,13 +47,14 @@ public:
TimestampsUpdateVisitor()
: ArtifactVisitor(Artifact::Generated), m_now(FileTime::currentTime()) {}
- void visitProduct(const BuildProductConstPtr &product)
+ void visitProduct(const ResolvedProductPtr &product)
{
+ QBS_CHECK(product->buildData);
ArtifactVisitor::visitProduct(product);
// For target artifacts, we have to update the on-disk timestamp, because
// the executor will look at it.
- foreach (Artifact * const targetArtifact, product->targetArtifacts) {
+ foreach (Artifact * const targetArtifact, product->buildData->targetArtifacts) {
if (FileInfo(targetArtifact->filePath()).exists())
QFile(targetArtifact->filePath()).open(QIODevice::WriteOnly | QIODevice::Append);
}
@@ -69,14 +70,15 @@ private:
FileTime m_now;
};
-void TimestampsUpdater::updateTimestamps(const QList<BuildProductPtr> &products)
+void TimestampsUpdater::updateTimestamps(const QList<ResolvedProductPtr> &products,
+ const Logger &logger)
{
TimestampsUpdateVisitor v;
- foreach (const BuildProductPtr &product, products)
+ foreach (const ResolvedProductPtr &product, products)
v.visitProduct(product);
- BuildProject * const project = products.first()->project;
- project->markDirty();
- project->store();
+ const ResolvedProjectPtr project = products.first()->project;
+ project->buildData->isDirty = true;
+ project->store(logger);
}
} // namespace Internal
diff --git a/src/lib/buildgraph/timestampsupdater.h b/src/lib/buildgraph/timestampsupdater.h
index 693d5959c..fbc98374a 100644
--- a/src/lib/buildgraph/timestampsupdater.h
+++ b/src/lib/buildgraph/timestampsupdater.h
@@ -29,17 +29,18 @@
#ifndef TIMESTAMPSUPDATER_H
#define TIMESTAMPSUPDATER_H
-#include "forward_decls.h"
+#include <language/forward_decls.h>
#include <QList>
namespace qbs {
namespace Internal {
+class Logger;
class TimestampsUpdater
{
public:
- void updateTimestamps(const QList<BuildProductPtr> &products);
+ void updateTimestamps(const QList<ResolvedProductPtr> &products, const Logger &logger);
};
} // namespace Internal
diff --git a/src/lib/buildgraph/tst_buildgraph.cpp b/src/lib/buildgraph/tst_buildgraph.cpp
index 3c9c208ff..ff78860ec 100644
--- a/src/lib/buildgraph/tst_buildgraph.cpp
+++ b/src/lib/buildgraph/tst_buildgraph.cpp
@@ -29,7 +29,7 @@
#include "tst_buildgraph.h"
#include <buildgraph/artifact.h>
-#include <buildgraph/buildproduct.h>
+#include <buildgraph/productbuilddata.h>
#include <buildgraph/cycledetector.h>
#include <language/language.h>
#include <logging/logger.h>
@@ -54,7 +54,7 @@ void TestBuildGraph::cleanupTestCase()
}
-bool TestBuildGraph::cycleDetected(const BuildProductConstPtr &product)
+bool TestBuildGraph::cycleDetected(const ResolvedProductConstPtr &product)
{
try {
CycleDetector(Logger(m_logSink)).visitProduct(product);
@@ -64,19 +64,20 @@ bool TestBuildGraph::cycleDetected(const BuildProductConstPtr &product)
}
}
-BuildProductConstPtr TestBuildGraph::productWithDirectCycle()
+ResolvedProductConstPtr TestBuildGraph::productWithDirectCycle()
{
Artifact * const root = new Artifact;
Artifact * const child = new Artifact;
m_artifacts << root << child;
root->children.insert(child);
child->children.insert(root);
- const BuildProductPtr product = BuildProduct::create();
- product->targetArtifacts.insert(root);
+ const ResolvedProductPtr product = ResolvedProduct::create();
+ product->buildData.reset(new ProductBuildData);
+ product->buildData->targetArtifacts.insert(root);
return product;
}
-BuildProductConstPtr TestBuildGraph::productWithLessDirectCycle()
+ResolvedProductConstPtr TestBuildGraph::productWithLessDirectCycle()
{
Artifact * const root = new Artifact;
Artifact * const child = new Artifact;
@@ -85,20 +86,22 @@ BuildProductConstPtr TestBuildGraph::productWithLessDirectCycle()
root->children.insert(child);
child->children.insert(grandchild);
grandchild->children.insert(root);
- const BuildProductPtr product = BuildProduct::create();
- product->targetArtifacts << root;
+ const ResolvedProductPtr product = ResolvedProduct::create();
+ product->buildData.reset(new ProductBuildData);
+ product->buildData->targetArtifacts << root;
return product;
}
// root appears as a child, but in a different tree
-BuildProductConstPtr TestBuildGraph::productWithNoCycle()
+ResolvedProductConstPtr TestBuildGraph::productWithNoCycle()
{
Artifact * const root = new Artifact;
Artifact * const root2 = new Artifact;
m_artifacts << root << root2;
root2->children.insert(root);
- const BuildProductPtr product = BuildProduct::create();
- product->targetArtifacts << root << root2;
+ const ResolvedProductPtr product = ResolvedProduct::create();
+ product->buildData.reset(new ProductBuildData);
+ product->buildData->targetArtifacts << root << root2;
return product;
}
diff --git a/src/lib/buildgraph/tst_buildgraph.h b/src/lib/buildgraph/tst_buildgraph.h
index da28ef813..35b3ea0d7 100644
--- a/src/lib/buildgraph/tst_buildgraph.h
+++ b/src/lib/buildgraph/tst_buildgraph.h
@@ -30,6 +30,7 @@
#define TST_BUILDGRAPH_H
#include <buildgraph/forward_decls.h>
+#include <language/forward_decls.h>
#include <logging/ilogsink.h>
#include <tools/qbs_export.h>
@@ -51,10 +52,10 @@ private slots:
void testCycle();
private:
- BuildProductConstPtr productWithDirectCycle();
- BuildProductConstPtr productWithLessDirectCycle();
- BuildProductConstPtr productWithNoCycle();
- bool cycleDetected(const BuildProductConstPtr &product);
+ ResolvedProductConstPtr productWithDirectCycle();
+ ResolvedProductConstPtr productWithLessDirectCycle();
+ ResolvedProductConstPtr productWithNoCycle();
+ bool cycleDetected(const ResolvedProductConstPtr &product);
QList<Artifact *> m_artifacts;
ILogSink * const m_logSink;
diff --git a/src/lib/language/language.cpp b/src/lib/language/language.cpp
index 0cbf94437..08db12ec3 100644
--- a/src/lib/language/language.cpp
+++ b/src/lib/language/language.cpp
@@ -31,6 +31,10 @@
#include "artifactproperties.h"
#include "scriptengine.h"
+#include <buildgraph/artifact.h>
+#include <buildgraph/productbuilddata.h>
+#include <buildgraph/projectbuilddata.h>
+#include <buildgraph/rulegraph.h> // TODO: Move to language?
#include <logging/translator.h>
#include <tools/hostosinfo.h>
#include <tools/error.h>
@@ -366,6 +370,7 @@ void ResolvedProduct::load(PersistentPool &pool)
pool.loadContainerS(modules);
pool.loadContainerS(groups);
pool.loadContainerS(artifactProperties);
+ buildData.reset(pool.idLoad<ProductBuildData>());
}
void ResolvedProduct::store(PersistentPool &pool) const
@@ -387,6 +392,7 @@ void ResolvedProduct::store(PersistentPool &pool) const
pool.storeContainer(modules);
pool.storeContainer(groups);
pool.storeContainer(artifactProperties);
+ pool.store(buildData.data());
}
QList<const ResolvedModule*> topSortModules(const QHash<const ResolvedModule*, QList<const ResolvedModule*> > &moduleChildren,
@@ -546,6 +552,23 @@ void ResolvedProduct::setupRunEnvironment(ScriptEngine *engine, const QProcessEn
runEnvironment = getProcessEnvironment(engine, RunEnv, modules, properties, project, env);
}
+const QList<RuleConstPtr> &ResolvedProduct::topSortedRules() const
+{
+ if (buildData->topSortedRules.isEmpty()) {
+ FileTags productFileTags = fileTags;
+ productFileTags += additionalFileTags;
+ RuleGraph ruleGraph;
+ ruleGraph.build(rules, productFileTags);
+// ruleGraph.dump();
+ buildData->topSortedRules = ruleGraph.topSorted();
+// int i=0;
+// foreach (RulePtr r, m_topSortedRules)
+// qDebug() << ++i << r->toString() << (void*)r.data();
+ }
+ return buildData->topSortedRules;
+}
+
+
QString ResolvedProject::deriveId(const QVariantMap &config)
{
const QVariantMap qbsProperties = config.value(QLatin1String("qbs")).toMap();
@@ -565,6 +588,40 @@ void ResolvedProject::setBuildConfiguration(const QVariantMap &config)
m_id = deriveId(config);
}
+QString ResolvedProject::buildGraphFilePath() const
+{
+ return ProjectBuildData::deriveBuildGraphFilePath(buildDirectory, id());
+}
+
+void ResolvedProject::store(const Logger &logger) const
+{
+ // TODO: Use progress observer here.
+
+ if (!buildData)
+ return;
+ if (!buildData->isDirty) {
+ logger.qbsDebug() << "[BG] build graph is unchanged in project " << id() << ".";
+ return;
+ }
+ const QString fileName = buildGraphFilePath();
+ logger.qbsDebug() << "[BG] storing: " << fileName;
+ PersistentPool pool(logger);
+ PersistentPool::HeadData headData;
+ headData.projectConfig = buildConfiguration();
+ pool.setHeadData(headData);
+ pool.setupWriteStream(fileName);
+ store(pool);
+ buildData->isDirty = false;
+}
+
+ResolvedProject::ResolvedProject()
+{
+}
+
+ResolvedProject::~ResolvedProject()
+{
+}
+
void ResolvedProject::load(PersistentPool &pool)
{
location.fileName = pool.idLoadString();
@@ -579,8 +636,13 @@ void ResolvedProject::load(PersistentPool &pool)
products.reserve(count);
for (; --count >= 0;) {
ResolvedProductPtr rProduct = pool.idLoadS<ResolvedProduct>();
+ if (rProduct->buildData) {
+ foreach (Artifact * const a, rProduct->buildData->artifacts)
+ a->product = rProduct;
+ }
products.append(rProduct);
}
+ buildData.reset(pool.idLoad<ProjectBuildData>());
}
void ResolvedProject::store(PersistentPool &pool) const
@@ -594,6 +656,7 @@ void ResolvedProject::store(PersistentPool &pool) const
pool.stream() << products.count();
foreach (const ResolvedProductConstPtr &product, products)
pool.store(product);
+ pool.store(buildData.data());
}
/*!
diff --git a/src/lib/language/language.h b/src/lib/language/language.h
index 439f29166..688e99960 100644
--- a/src/lib/language/language.h
+++ b/src/lib/language/language.h
@@ -34,6 +34,7 @@
#include "forward_decls.h"
#include "jsimports.h"
#include "propertymapinternal.h"
+#include <buildgraph/forward_decls.h>
#include <tools/codelocation.h>
#include <tools/fileinfo.h>
#include <tools/persistentobject.h>
@@ -44,6 +45,7 @@
#include <QProcessEnvironment>
#include <QScriptProgram>
#include <QScriptValue>
+#include <QScopedPointer>
#include <QSet>
#include <QString>
#include <QStringList>
@@ -55,6 +57,7 @@ QT_END_NAMESPACE
namespace qbs {
namespace Internal {
+class BuildGraphLoader;
class FileTagger : public PersistentObject
{
@@ -295,6 +298,7 @@ public:
QList<ResolvedTransformer::Ptr> transformers;
QList<GroupPtr> groups;
QList<ArtifactPropertiesPtr> artifactProperties;
+ QScopedPointer<ProductBuildData> buildData;
mutable QProcessEnvironment buildEnvironment; // must not be saved
mutable QProcessEnvironment runEnvironment; // must not be saved
@@ -306,6 +310,8 @@ public:
void setupBuildEnvironment(ScriptEngine *scriptEngine, const QProcessEnvironment &env) const;
void setupRunEnvironment(ScriptEngine *scriptEngine, const QProcessEnvironment &env) const;
+ const QList<RuleConstPtr> &topSortedRules() const;
+
private:
ResolvedProduct();
@@ -315,7 +321,10 @@ private:
class ResolvedProject: public PersistentObject
{
+ friend class BuildGraphLoader;
public:
+ ~ResolvedProject();
+
static ResolvedProjectPtr create() { return ResolvedProjectPtr(new ResolvedProject); }
static QString deriveId(const QVariantMap &config);
@@ -325,13 +334,17 @@ public:
QString buildDirectory; // Not saved
QVariantMap platformEnvironment;
QList<ResolvedProductPtr> products;
+ QScopedPointer<ProjectBuildData> buildData;
void setBuildConfiguration(const QVariantMap &config);
const QVariantMap &buildConfiguration() const { return m_buildConfiguration; }
QString id() const { return m_id; }
+ QString buildGraphFilePath() const;
+ void store(const Logger &logger) const;
+
private:
- ResolvedProject() {}
+ ResolvedProject();
void load(PersistentPool &pool);
void store(PersistentPool &pool) const;
diff --git a/src/lib/lib.qbs b/src/lib/lib.qbs
index 6518410b7..da53e490b 100644
--- a/src/lib/lib.qbs
+++ b/src/lib/lib.qbs
@@ -75,10 +75,6 @@ DynamicLibrary {
"automoc.h",
"buildgraph.cpp",
"buildgraph.h",
- "buildproduct.cpp",
- "buildproduct.h",
- "buildproject.cpp",
- "buildproject.h",
"command.cpp",
"command.h",
"cycledetector.cpp",
@@ -93,8 +89,12 @@ DynamicLibrary {
"jscommandexecutor.h",
"processcommandexecutor.cpp",
"processcommandexecutor.h",
+ "productbuilddata.cpp",
+ "productbuilddata.h",
"productinstaller.cpp",
"productinstaller.h",
+ "projectbuilddata.cpp",
+ "projectbuilddata.h",
"rulegraph.cpp",
"rulegraph.h",
"rulesapplicator.cpp",
diff --git a/src/lib/tools/persistence.cpp b/src/lib/tools/persistence.cpp
index ee65f64e3..a4be67ad8 100644
--- a/src/lib/tools/persistence.cpp
+++ b/src/lib/tools/persistence.cpp
@@ -39,7 +39,7 @@
namespace qbs {
namespace Internal {
-static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE0_0_1__35";
+static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE0_0_1__36";
PersistentPool::PersistentPool(const Logger &logger) : m_logger(logger)
{