aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@digia.com>2012-11-29 17:55:50 +0100
committerChristian Kandeler <christian.kandeler@digia.com>2012-12-04 11:49:46 +0100
commitc552cb2ab5c3be9d2c6209b076a71c9e37209c29 (patch)
treeddfb4e866597e4856b52ba0a9c7b10e023418df1
parenta1d3d727f53874451bb07e66f275969d9b0e9b02 (diff)
Split up buildgraph.h and buildgraph.cpp.
Move things to the appropriate places. Change-Id: I2f201ccdf9c465a8c3201e229629b756cc258495 Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
-rw-r--r--src/lib/api/internaljobs.cpp3
-rw-r--r--src/lib/api/jobs.cpp2
-rw-r--r--src/lib/api/project.cpp4
-rw-r--r--src/lib/api/runenvironment.cpp1
-rw-r--r--src/lib/buildgraph/artifact.cpp4
-rw-r--r--src/lib/buildgraph/artifactcleaner.cpp3
-rw-r--r--src/lib/buildgraph/artifactvisitor.cpp3
-rw-r--r--src/lib/buildgraph/automoc.cpp36
-rw-r--r--src/lib/buildgraph/buildgraph.cpp1156
-rw-r--r--src/lib/buildgraph/buildgraph.h196
-rw-r--r--src/lib/buildgraph/buildgraph.pri10
-rw-r--r--src/lib/buildgraph/buildproduct.cpp233
-rw-r--r--src/lib/buildgraph/buildproduct.h78
-rw-r--r--src/lib/buildgraph/buildproject.cpp673
-rw-r--r--src/lib/buildgraph/buildproject.h152
-rw-r--r--src/lib/buildgraph/cycledetector.cpp2
-rw-r--r--src/lib/buildgraph/executor.cpp15
-rw-r--r--src/lib/buildgraph/executorjob.cpp2
-rw-r--r--src/lib/buildgraph/inputartifactscanner.cpp3
-rw-r--r--src/lib/buildgraph/jscommandexecutor.cpp1
-rw-r--r--src/lib/buildgraph/processcommandexecutor.cpp2
-rw-r--r--src/lib/buildgraph/rulesapplicator.cpp310
-rw-r--r--src/lib/buildgraph/rulesapplicator.h74
-rw-r--r--src/lib/buildgraph/rulesevaluationcontext.cpp1
-rw-r--r--src/lib/buildgraph/transformer.cpp29
-rw-r--r--tests/auto/buildgraph/tst_buildgraph.cpp2
26 files changed, 1632 insertions, 1363 deletions
diff --git a/src/lib/api/internaljobs.cpp b/src/lib/api/internaljobs.cpp
index 19543cc8f..fcd830585 100644
--- a/src/lib/api/internaljobs.cpp
+++ b/src/lib/api/internaljobs.cpp
@@ -29,7 +29,8 @@
#include "internaljobs.h"
#include <buildgraph/artifactcleaner.h>
-#include <buildgraph/buildgraph.h>
+#include <buildgraph/buildproduct.h>
+#include <buildgraph/buildproject.h>
#include <buildgraph/executor.h>
#include <buildgraph/rulesevaluationcontext.h>
#include <language/language.h>
diff --git a/src/lib/api/jobs.cpp b/src/lib/api/jobs.cpp
index 6ad8db5d9..84d2e29ae 100644
--- a/src/lib/api/jobs.cpp
+++ b/src/lib/api/jobs.cpp
@@ -30,7 +30,7 @@
#include "internaljobs.h"
#include "project.h"
-#include <buildgraph/buildgraph.h>
+#include <buildgraph/buildproject.h>
#include <language/language.h>
#include <language/scriptengine.h>
diff --git a/src/lib/api/project.cpp b/src/lib/api/project.cpp
index a83a6e1ea..586aa7507 100644
--- a/src/lib/api/project.cpp
+++ b/src/lib/api/project.cpp
@@ -32,7 +32,8 @@
#include "projectdata.h"
#include "runenvironment.h"
#include <buildgraph/artifact.h>
-#include <buildgraph/buildgraph.h>
+#include <buildgraph/buildproduct.h>
+#include <buildgraph/buildproject.h>
#include <language/language.h>
#include <logging/logger.h>
#include <logging/translator.h>
@@ -43,6 +44,7 @@
#include <tools/scripttools.h>
#include <tools/settings.h>
+#include <QDir>
#include <QMutex>
#include <QMutexLocker>
#include <QSharedData>
diff --git a/src/lib/api/runenvironment.cpp b/src/lib/api/runenvironment.cpp
index 1f279f6dc..cc59a91b3 100644
--- a/src/lib/api/runenvironment.cpp
+++ b/src/lib/api/runenvironment.cpp
@@ -30,7 +30,6 @@
#include "runenvironment.h"
#include <api/projectdata.h>
-#include <buildgraph/buildgraph.h>
#include <language/language.h>
#include <language/scriptengine.h>
#include <logging/logger.h>
diff --git a/src/lib/buildgraph/artifact.cpp b/src/lib/buildgraph/artifact.cpp
index 86e996126..f522ec356 100644
--- a/src/lib/buildgraph/artifact.cpp
+++ b/src/lib/buildgraph/artifact.cpp
@@ -109,7 +109,7 @@ void Artifact::store(PersistentPool &pool) const
void Artifact::disconnectChildren()
{
if (qbsLogLevel(LoggerTrace))
- qbsTrace("[BG] disconnectChildren: '%s'", qPrintable(qbs::Internal::fileName(this)));
+ qbsTrace("[BG] disconnectChildren: '%s'", qPrintable(BuildGraph::fileName(this)));
foreach (Artifact * const child, children)
child->parents.remove(this);
children.clear();
@@ -118,7 +118,7 @@ void Artifact::disconnectChildren()
void Artifact::disconnectParents()
{
if (qbsLogLevel(LoggerTrace))
- qbsTrace("[BG] disconnectParents: '%s'", qPrintable(qbs::Internal::fileName(this)));
+ qbsTrace("[BG] disconnectParents: '%s'", qPrintable(BuildGraph::fileName(this)));
foreach (Artifact * const parent, parents)
parent->children.remove(this);
parents.clear();
diff --git a/src/lib/buildgraph/artifactcleaner.cpp b/src/lib/buildgraph/artifactcleaner.cpp
index 5a049e4ca..fc5702877 100644
--- a/src/lib/buildgraph/artifactcleaner.cpp
+++ b/src/lib/buildgraph/artifactcleaner.cpp
@@ -30,7 +30,8 @@
#include "artifact.h"
#include "artifactvisitor.h"
-#include "buildgraph.h"
+#include "buildproduct.h"
+#include "buildproject.h"
#include "transformer.h"
#include <language/language.h>
diff --git a/src/lib/buildgraph/artifactvisitor.cpp b/src/lib/buildgraph/artifactvisitor.cpp
index c1b8651ac..5c9ff8154 100644
--- a/src/lib/buildgraph/artifactvisitor.cpp
+++ b/src/lib/buildgraph/artifactvisitor.cpp
@@ -29,7 +29,8 @@
#include "artifactvisitor.h"
#include "artifact.h"
-#include "buildgraph.h"
+#include "buildproduct.h"
+#include "buildproject.h"
#include <language/language.h>
namespace qbs {
diff --git a/src/lib/buildgraph/automoc.cpp b/src/lib/buildgraph/automoc.cpp
index 070b75e17..eb797670d 100644
--- a/src/lib/buildgraph/automoc.cpp
+++ b/src/lib/buildgraph/automoc.cpp
@@ -28,7 +28,10 @@
****************************************************************************/
#include "automoc.h"
+#include "buildproduct.h"
+#include "buildproject.h"
#include "buildgraph.h"
+#include "rulesapplicator.h"
#include "scanresultcache.h"
#include <buildgraph/artifact.h>
#include <buildgraph/transformer.h>
@@ -41,6 +44,29 @@
namespace qbs {
namespace Internal {
+static char **createCFileTags(const QSet<QString> &fileTags)
+{
+ if (fileTags.isEmpty())
+ return 0;
+
+ char **buf = new char*[fileTags.count()];
+ size_t i = 0;
+ foreach (const QString &fileTag, fileTags) {
+ buf[i] = qstrdup(fileTag.toLocal8Bit().data());
+ ++i;
+ }
+ return buf;
+}
+
+static void freeCFileTags(char **cFileTags, int numFileTags)
+{
+ if (!cFileTags)
+ return;
+ for (int i = numFileTags; --i >= 0;)
+ delete[] cFileTags[i];
+ delete[] cFileTags;
+}
+
AutoMoc::AutoMoc()
: m_scanResultCache(0)
{
@@ -130,7 +156,7 @@ void AutoMoc::apply(const BuildProductPtr &product)
artifactsPerFileTag[QLatin1String("c++_pch")] += pchFile;
if (!artifactsPerFileTag.isEmpty()) {
qbsInfo() << DontPrintLogLevel << "Applying moc rules for '" << product->rProduct->name << "'.";
- product->applyRules(artifactsPerFileTag);
+ RulesApplicator(product.data(), artifactsPerFileTag).applyAllRules();
}
if (pluginHeaderFile && pluginMetaDataFile) {
// Make every artifact that is dependent of the header file also
@@ -171,7 +197,7 @@ AutoMoc::FileType AutoMoc::fileType(Artifact *artifact)
void AutoMoc::scan(Artifact *artifact, bool &hasQObjectMacro, QSet<QString> &includedMocCppFiles)
{
if (qbsLogLevel(LoggerTrace))
- qbsTrace() << "[AUTOMOC] checks " << fileName(artifact);
+ qbsTrace() << "[AUTOMOC] checks " << BuildGraph::fileName(artifact);
hasQObjectMacro = false;
const int numFileTags = artifact->fileTags.count();
@@ -266,7 +292,7 @@ bool AutoMoc::isVictimOfMoc(Artifact *artifact, FileType fileType, QString &foun
void AutoMoc::unmoc(Artifact *artifact, const QString &mocFileTag)
{
if (qbsLogLevel(LoggerTrace))
- qbsTrace() << "[AUTOMOC] unmoc'ing " << fileName(artifact);
+ qbsTrace() << "[AUTOMOC] unmoc'ing " << BuildGraph::fileName(artifact);
artifact->fileTags.remove(mocFileTag);
@@ -300,13 +326,13 @@ void AutoMoc::unmoc(Artifact *artifact, const QString &mocFileTag)
qbsTrace() << "[AUTOMOC] generated moc obj artifact could not be found";
} else {
if (qbsLogLevel(LoggerTrace))
- qbsTrace() << "[AUTOMOC] removing moc obj artifact " << fileName(mocObjArtifact);
+ qbsTrace() << "[AUTOMOC] removing moc obj artifact " << BuildGraph::fileName(mocObjArtifact);
artifact->project->removeArtifact(mocObjArtifact);
}
}
if (qbsLogLevel(LoggerTrace))
- qbsTrace() << "[AUTOMOC] removing generated artifact " << fileName(generatedMocArtifact);
+ qbsTrace() << "[AUTOMOC] removing generated artifact " << BuildGraph::fileName(generatedMocArtifact);
artifact->project->removeArtifact(generatedMocArtifact);
delete generatedMocArtifact;
}
diff --git a/src/lib/buildgraph/buildgraph.cpp b/src/lib/buildgraph/buildgraph.cpp
index 4e79acd0f..e9c88ed0e 100644
--- a/src/lib/buildgraph/buildgraph.cpp
+++ b/src/lib/buildgraph/buildgraph.cpp
@@ -26,162 +26,21 @@
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
-
#include "buildgraph.h"
#include "artifact.h"
-#include "cycledetector.h"
-#include "command.h"
-#include "rulegraph.h"
-#include "rulesevaluationcontext.h"
-#include "transformer.h"
+#include "buildproject.h"
#include <language/language.h>
-#include <language/loader.h>
#include <language/scriptengine.h>
#include <logging/logger.h>
-#include <logging/translator.h>
#include <tools/fileinfo.h>
-#include <tools/persistence.h>
-#include <tools/scannerpluginmanager.h>
-#include <tools/progressobserver.h>
-#include <tools/scripttools.h>
-#include <QCache>
-#include <QDir>
-#include <QDirIterator>
-#include <QDataStream>
-#include <QFileInfo>
-#include <QMutex>
-#include <QScriptProgram>
-#include <QScriptValueIterator>
-#include <QThread>
+#include <QFile>
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(QStringList(artifact->fileTags.toList()).join(",")));
- 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()) {
- QStringList fileTags;
- fileTags << rProduct->fileTags << rProduct->additionalFileTags;
- fileTags.sort();
- std::unique(fileTags.begin(), fileTags.end());
- RuleGraph ruleGraph;
- ruleGraph.build(rProduct->rules, fileTags);
-// 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::createArtifact(const SourceArtifactConstPtr &sourceArtifact)
-{
- Artifact *artifact = new Artifact(project);
- artifact->artifactType = Artifact::SourceFile;
- artifact->setFilePath(sourceArtifact->absoluteFilePath);
- artifact->fileTags = sourceArtifact->fileTags;
- artifact->properties = sourceArtifact->properties;
- insertArtifact(artifact);
- return artifact;
-}
-
-void BuildProduct::insertArtifact(Artifact *artifact)
-{
- Q_ASSERT(!artifact->product);
- Q_ASSERT(!artifact->filePath().isEmpty());
- Q_ASSERT(!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 (qbsLogLevel(LoggerTrace))
- qbsTrace("[BG] insert artifact '%s'", qPrintable(artifact->filePath()));
-}
-
-void BuildProduct::applyRules(ArtifactsPerFileTagMap &artifactsPerFileTag)
-{
- RulesEvaluationContext::Scope s(project->evaluationContext());
- RulesApplicator rulesApplier(this, artifactsPerFileTag);
- rulesApplier.applyAllRules();
-}
-
-
void BuildGraph::setupScriptEngineForProduct(ScriptEngine *engine,
const ResolvedProductConstPtr &product,
const RuleConstPtr &rule,
@@ -319,385 +178,7 @@ void BuildGraph::removeGeneratedArtifactFromDisk(Artifact *artifact)
qbsWarning("Cannot remove '%s'.", qPrintable(artifact->filePath()));
}
-void Transformer::load(PersistentPool &pool)
-{
- rule = pool.idLoadS<Rule>();
- pool.loadContainer(inputs);
- pool.loadContainer(outputs);
- int count, cmdType;
- pool.stream() >> count;
- commands.reserve(count);
- while (--count >= 0) {
- pool.stream() >> cmdType;
- AbstractCommand *cmd = AbstractCommand::createByType(static_cast<AbstractCommand::CommandType>(cmdType));
- cmd->load(pool.stream());
- commands += cmd;
- }
-}
-
-void Transformer::store(PersistentPool &pool) const
-{
- pool.store(rule);
- pool.storeContainer(inputs);
- pool.storeContainer(outputs);
- pool.stream() << commands.count();
- foreach (AbstractCommand *cmd, commands) {
- pool.stream() << int(cmd->type());
- cmd->store(pool.stream());
- }
-}
-
-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);
-}
-
-BuildProject::BuildProject() : m_evalContext(0), m_dirty(false)
-{
-}
-
-BuildProject::~BuildProject()
-{
- delete m_evalContext;
- qDeleteAll(m_dependencyArtifacts);
-}
-
-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;
-}
-
-void BuildProject::store() const
-{
- if (!dirty()) {
- qbsDebug() << "[BG] build graph is unchanged in project " << resolvedProject()->id() << ".";
- return;
- }
- const QString fileName = buildGraphFilePath();
- qbsDebug() << "[BG] storing: " << fileName;
- PersistentPool pool;
- 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(RulesEvaluationContext *evalContext)
-{
- delete m_evalContext;
- m_evalContext = evalContext;
-}
-
-void BuildProject::load(PersistentPool &pool)
-{
- TimedActivityLogger 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);
-}
-
-char **createCFileTags(const QSet<QString> &fileTags)
-{
- if (fileTags.isEmpty())
- return 0;
-
- char **buf = new char*[fileTags.count()];
- size_t i = 0;
- foreach (const QString &fileTag, fileTags) {
- buf[i] = qstrdup(fileTag.toLocal8Bit().data());
- ++i;
- }
- return buf;
-}
-
-void freeCFileTags(char **cFileTags, int numFileTags)
-{
- if (!cFileTags)
- return;
- for (int i = numFileTags; --i >= 0;)
- delete[] cFileTags[i];
- delete[] cFileTags;
-}
-
-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;
-}
-
-void BuildProject::insertFileDependency(Artifact *artifact)
-{
- Q_ASSERT(artifact->artifactType == Artifact::FileDependency);
- m_dependencyArtifacts += artifact;
- insertIntoArtifactLookupTable(artifact);
-}
-
-/**
- * Copies dependencies between artifacts from the other project to this project.
- */
-void BuildProject::rescueDependencies(const BuildProjectPtr &other)
-{
- QHash<QString, BuildProductPtr> otherProductsByName;
- foreach (const BuildProductPtr &product, other->m_buildProducts)
- otherProductsByName.insert(product->rProduct->name, product);
-
- foreach (const BuildProductPtr &product, m_buildProducts) {
- BuildProductPtr otherProduct = otherProductsByName.value(product->rProduct->name);
- if (!otherProduct)
- continue;
-
- if (qbsLogLevel(LoggerTrace))
- qbsTrace("[BG] rescue dependencies of product '%s'", qPrintable(product->rProduct->name));
-
- foreach (Artifact *artifact, product->artifacts) {
- if (qbsLogLevel(LoggerTrace))
- qbsTrace("[BG] artifact '%s'", qPrintable(artifact->fileName()));
-
- Artifact *otherArtifact = otherProduct->lookupArtifact(artifact->dirPath(), artifact->fileName());
- if (!otherArtifact || !otherArtifact->transformer)
- continue;
-
- foreach (Artifact *otherChild, otherArtifact->children) {
- // skip transform edges
- if (otherArtifact->transformer->inputs.contains(otherChild))
- continue;
-
- QList<Artifact *> children = lookupArtifacts(otherChild->dirPath(), otherChild->fileName());
- foreach (Artifact *child, children) {
- if (!artifact->children.contains(child))
- BuildGraph::safeConnect(artifact, child);
- }
- }
- }
- }
-}
-
-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 (qbsLogLevel(LoggerTrace))
- qbsTrace() << "[BG] remove artifact " << fileName(artifact);
-
- BuildGraph::removeGeneratedArtifactFromDisk(artifact);
- 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());
- foreach (Artifact *artifact, m_artifactsThatMustGetNewTransformers)
- updateNodeThatMustGetNewTransformer(artifact);
- m_artifactsThatMustGetNewTransformers.clear();
-}
-
-void BuildProject::updateNodeThatMustGetNewTransformer(Artifact *artifact)
-{
- Q_ASSERT(artifact->transformer);
-
- if (qbsLogLevel(LoggerDebug))
- qbsDebug() << "[BG] updating transformer for " << fileName(artifact);
-
- BuildGraph::removeGeneratedArtifactFromDisk(artifact);
-
- const RuleConstPtr rule = artifact->transformer->rule;
- markDirty();
- artifact->transformer = TransformerPtr();
-
- ArtifactsPerFileTagMap artifactsPerFileTag;
- foreach (Artifact *input, artifact->children) {
- foreach (const QString &fileTag, input->fileTags)
- artifactsPerFileTag[fileTag] += input;
- }
- RulesApplicator rulesApplier(artifact->product, artifactsPerFileTag);
- rulesApplier.applyRule(rule);
-}
-
-
-QString fileName(Artifact *n)
+QString BuildGraph::fileName(const Artifact *n)
{
const QString &buildDir = n->project->resolvedProject()->buildDirectory;
QString str = n->filePath();
@@ -708,636 +189,5 @@ QString fileName(Artifact *n)
return str;
}
-RulesApplicator::RulesApplicator(BuildProduct *product, ArtifactsPerFileTagMap &artifactsPerFileTag)
- : m_buildProduct(product)
- , m_artifactsPerFileTag(artifactsPerFileTag)
-{
-}
-
-void RulesApplicator::applyAllRules()
-{
- foreach (const RuleConstPtr &rule, m_buildProduct->topSortedRules())
- applyRule(rule);
-}
-
-void RulesApplicator::applyRule(const RuleConstPtr &rule)
-{
- m_rule = rule;
- BuildGraph::setupScriptEngineForProduct(engine(), m_buildProduct->rProduct, m_rule, scope());
- Q_ASSERT_X(scope().property("product").strictlyEquals(engine()->evaluate("product")),
- "BG", "Product object is not in current scope.");
-
- ArtifactList inputArtifacts;
- foreach (const QString &fileTag, m_rule->inputs)
- inputArtifacts.unite(m_artifactsPerFileTag.value(fileTag));
- if (m_rule->multiplex) { // apply the rule once for a set of inputs
- if (!inputArtifacts.isEmpty())
- doApply(inputArtifacts);
- } else { // apply the rule once for each input
- ArtifactList lst;
- foreach (Artifact * const inputArtifact, inputArtifacts) {
- setupScriptEngineForArtifact(inputArtifact);
- lst += inputArtifact;
- doApply(lst);
- lst.clear();
- }
- }
-}
-
-void RulesApplicator::doApply(const ArtifactList &inputArtifacts)
-{
- evalContext()->checkForCancelation();
-
- if (qbsLogLevel(LoggerDebug))
- qbsDebug() << "[BG] apply rule " << m_rule->toString() << " "
- << toStringList(inputArtifacts).join(",\n ");
-
- QList<QPair<const RuleArtifact *, Artifact *> > ruleArtifactArtifactMap;
- QList<Artifact *> outputArtifacts;
-
- ArtifactList usingArtifacts;
- if (!m_rule->usings.isEmpty()) {
- const QSet<QString> usingsFileTags = m_rule->usings.toSet();
- foreach (const BuildProductPtr &dep, m_buildProduct->dependencies) {
- ArtifactList artifactsToCheck;
- foreach (Artifact *targetArtifact, dep->targetArtifacts)
- artifactsToCheck.unite(targetArtifact->transformer->outputs);
- foreach (Artifact *artifact, artifactsToCheck) {
- QSet<QString> matchingFileTags = artifact->fileTags;
- matchingFileTags.intersect(usingsFileTags);
- if (!matchingFileTags.isEmpty())
- usingArtifacts.insert(artifact);
- }
- }
- }
-
- m_transformer.clear();
- // create the output artifacts from the set of input artifacts
- foreach (const RuleArtifactConstPtr &ruleArtifact, m_rule->artifacts) {
- Artifact * const outputArtifact = createOutputArtifact(ruleArtifact, inputArtifacts);
- outputArtifacts << outputArtifact;
- ruleArtifactArtifactMap << qMakePair(ruleArtifact.data(), outputArtifact);
- }
-
- foreach (Artifact *outputArtifact, outputArtifacts) {
- // insert the output artifacts into the pool of artifacts
- foreach (const QString &fileTag, outputArtifact->fileTags)
- m_artifactsPerFileTag[fileTag].insert(outputArtifact);
-
- // connect artifacts that match the file tags in explicitlyDependsOn
- foreach (const QString &fileTag, m_rule->explicitlyDependsOn)
- foreach (Artifact *dependency, m_artifactsPerFileTag.value(fileTag))
- BuildGraph::loggedConnect(outputArtifact, dependency);
-
- // Transformer setup
- for (ArtifactList::const_iterator it = usingArtifacts.constBegin();
- it != usingArtifacts.constEnd(); ++it)
- {
- Artifact *dep = *it;
- BuildGraph::loggedConnect(outputArtifact, dep);
- m_transformer->inputs.insert(dep);
- }
- m_transformer->outputs.insert(outputArtifact);
-
- m_buildProduct->project->removeFromArtifactsThatMustGetNewTransformers(outputArtifact);
- }
-
- m_transformer->setupInputs(engine(), scope());
-
- // change the transformer outputs according to the bindings in Artifact
- QScriptValue scriptValue;
- for (int i = ruleArtifactArtifactMap.count(); --i >= 0;) {
- const RuleArtifact *ra = ruleArtifactArtifactMap.at(i).first;
- if (ra->bindings.isEmpty())
- continue;
-
- // expose attributes of this artifact
- Artifact *outputArtifact = ruleArtifactArtifactMap.at(i).second;
- outputArtifact->properties = outputArtifact->properties->clone();
-
- scope().setProperty("fileName", engine()->toScriptValue(outputArtifact->filePath()));
- scope().setProperty("fileTags", toScriptValue(engine(), outputArtifact->fileTags));
-
- QVariantMap artifactModulesCfg = outputArtifact->properties->value().value("modules").toMap();
- for (int i=0; i < ra->bindings.count(); ++i) {
- const RuleArtifact::Binding &binding = ra->bindings.at(i);
- scriptValue = engine()->evaluate(binding.code);
- if (scriptValue.isError()) {
- QString msg = QLatin1String("evaluating rule binding '%1': %2");
- throw Error(msg.arg(binding.name.join(QLatin1String(".")), scriptValue.toString()), binding.location);
- }
- setConfigProperty(artifactModulesCfg, binding.name, scriptValue.toVariant());
- }
- QVariantMap outputArtifactConfig = outputArtifact->properties->value();
- outputArtifactConfig.insert("modules", artifactModulesCfg);
- outputArtifact->properties->setValue(outputArtifactConfig);
- }
-
- m_transformer->setupOutputs(engine(), scope());
- m_transformer->createCommands(m_rule->script, engine());
- if (m_transformer->commands.isEmpty())
- throw Error(QString("There's a rule without commands: %1.").arg(m_rule->toString()), m_rule->script->location);
-}
-
-void RulesApplicator::setupScriptEngineForArtifact(Artifact *artifact)
-{
- QString inFileName = artifact->fileName();
- QString inBaseName = FileInfo::baseName(artifact->filePath());
- QString inCompleteBaseName = FileInfo::completeBaseName(artifact->filePath());
-
- QString basedir;
- if (artifact->artifactType == Artifact::SourceFile) {
- QDir sourceDir(m_buildProduct->rProduct->sourceDirectory);
- basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->filePath()));
- } else {
- QDir buildDir(m_buildProduct->project->resolvedProject()->buildDirectory);
- basedir = FileInfo::path(buildDir.relativeFilePath(artifact->filePath()));
- }
-
- QScriptValue modulesScriptValue = artifact->properties->toScriptValue(engine());
- modulesScriptValue = modulesScriptValue.property("modules");
-
- // expose per file properties we want to use in an Artifact within a Rule
- QScriptValue scriptValue = engine()->newObject();
- scriptValue.setProperty("fileName", inFileName);
- scriptValue.setProperty("baseName", inBaseName);
- scriptValue.setProperty("completeBaseName", inCompleteBaseName);
- scriptValue.setProperty("baseDir", basedir);
- scriptValue.setProperty("modules", modulesScriptValue);
-
- scope().setProperty("input", scriptValue);
- Q_ASSERT_X(scriptValue.strictlyEquals(engine()->evaluate("input")),
- "BG", "The input object is not in current scope.");
-}
-
-Artifact *RulesApplicator::createOutputArtifact(const RuleArtifactConstPtr &ruleArtifact,
- const ArtifactList &inputArtifacts)
-{
- QScriptValue scriptValue = engine()->evaluate(ruleArtifact->fileName);
- if (scriptValue.isError() || engine()->hasUncaughtException())
- throw Error("Error in Rule.Artifact fileName: " + scriptValue.toString());
- QString outputPath = scriptValue.toString();
- outputPath.replace("..", "dotdot"); // don't let the output artifact "escape" its build dir
- outputPath = resolveOutPath(outputPath);
-
- Artifact *outputArtifact = m_buildProduct->lookupArtifact(outputPath);
- if (outputArtifact) {
- if (outputArtifact->transformer && outputArtifact->transformer != m_transformer) {
- // This can happen when applying rules after scanning for additional file tags.
- // We just regenerate the transformer.
- if (qbsLogLevel(LoggerTrace))
- qbsTrace("[BG] regenerating transformer for '%s'", qPrintable(fileName(outputArtifact)));
- m_transformer = outputArtifact->transformer;
- m_transformer->inputs.unite(inputArtifacts);
-
- if (m_transformer->inputs.count() > 1 && !m_rule->multiplex) {
- QString th = "[" + QStringList(outputArtifact->fileTags.toList()).join(", ") + "]";
- QString e = Tr::tr("Conflicting rules for producing %1 %2 \n").arg(outputArtifact->filePath(), th);
- th = "[" + m_rule->inputs.join(", ")
- + "] -> [" + QStringList(outputArtifact->fileTags.toList()).join(", ") + "]";
-
- e += QString(" while trying to apply: %1:%2:%3 %4\n")
- .arg(m_rule->script->location.fileName)
- .arg(m_rule->script->location.line)
- .arg(m_rule->script->location.column)
- .arg(th);
-
- e += QString(" was already defined in: %1:%2:%3 %4\n")
- .arg(outputArtifact->transformer->rule->script->location.fileName)
- .arg(outputArtifact->transformer->rule->script->location.line)
- .arg(outputArtifact->transformer->rule->script->location.column)
- .arg(th);
- throw Error(e);
- }
- }
- outputArtifact->fileTags += ruleArtifact->fileTags.toSet();
- } else {
- outputArtifact = new Artifact(m_buildProduct->project);
- outputArtifact->artifactType = Artifact::Generated;
- outputArtifact->setFilePath(outputPath);
- outputArtifact->fileTags = ruleArtifact->fileTags.toSet();
- outputArtifact->alwaysUpdated = ruleArtifact->alwaysUpdated;
- m_buildProduct->insertArtifact(outputArtifact);
- }
-
- if (outputArtifact->fileTags.isEmpty())
- outputArtifact->fileTags = m_buildProduct->rProduct->fileTagsForFileName(outputArtifact->fileName());
-
- if (m_rule->multiplex)
- outputArtifact->properties = m_buildProduct->rProduct->properties;
- else
- outputArtifact->properties= (*inputArtifacts.constBegin())->properties;
-
- foreach (Artifact *inputArtifact, inputArtifacts) {
- Q_ASSERT(outputArtifact != inputArtifact);
- BuildGraph::loggedConnect(outputArtifact, inputArtifact);
- }
-
- // create transformer if not already done so
- if (!m_transformer) {
- m_transformer = Transformer::create();
- m_transformer->rule = m_rule;
- m_transformer->inputs = inputArtifacts;
- }
- outputArtifact->transformer = m_transformer;
-
- return outputArtifact;
-}
-
-QString RulesApplicator::resolveOutPath(const QString &path) const
-{
- QString buildDir = m_buildProduct->project->resolvedProject()->buildDirectory;
- QString result = FileInfo::resolvePath(buildDir, path);
- result = QDir::cleanPath(result);
- return result;
-}
-
-RulesEvaluationContext *RulesApplicator::evalContext() const
-{
- return m_buildProduct->project->evaluationContext();
-}
-
-ScriptEngine *RulesApplicator::engine() const
-{
- return evalContext()->engine();
-}
-
-QScriptValue RulesApplicator::scope() const
-{
- return evalContext()->scope();
-}
-
-
-BuildProjectPtr BuildProjectResolver::resolveProject(const ResolvedProjectPtr &resolvedProject,
- RulesEvaluationContext *evalContext)
-{
- m_productCache.clear();
- m_project = BuildProjectPtr(new BuildProject);
- m_project->setEvaluationContext(evalContext);
- m_project->setResolvedProject(resolvedProject);
- evalContext->initializeObserver(Tr::tr("Resolving project"), resolvedProject->products.count());
- foreach (ResolvedProductPtr rProduct, resolvedProject->products) {
- resolveProduct(rProduct);
- evalContext->incrementProgressValue();
- }
- CycleDetector().visitProject(m_project);
- return m_project;
-}
-
-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"));
- 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);
- }
- qbsFileArtifact->fileTags.insert("qbs");
- artifactsPerFileTag["qbs"].insert(qbsFileArtifact);
-
- // read sources
- foreach (const SourceArtifactConstPtr &sourceArtifact, rProduct->allFiles()) {
- QString filePath = sourceArtifact->absoluteFilePath;
- if (product->lookupArtifact(filePath))
- continue; // ignore duplicate artifacts
-
- Artifact *artifact = product->createArtifact(sourceArtifact);
- foreach (const QString &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->inputs = rtrafo->inputs;
- 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);
- outputArtifact->artifactType = Artifact::Generated;
- outputArtifact->transformer = transformer;
- transformer->outputs += outputArtifact;
- product->targetArtifacts += outputArtifact;
- foreach (Artifact *inputArtifact, inputArtifacts)
- BuildGraph::safeConnect(outputArtifact, inputArtifact);
- foreach (const QString &fileTag, outputArtifact->fileTags)
- artifactsPerFileTag[fileTag].insert(outputArtifact);
-
- RuleArtifactPtr ruleArtifact = RuleArtifact::create();
- ruleArtifact->fileName = outputArtifact->filePath();
- ruleArtifact->fileTags = outputArtifact->fileTags.toList();
- rule->artifacts += ruleArtifact;
- }
- transformer->rule = rule;
-
- RulesEvaluationContext::Scope s(evalContext());
- BuildGraph::setupScriptEngineForProduct(engine(), rProduct, transformer->rule, scope());
- transformer->setupInputs(engine(), scope());
- transformer->setupOutputs(engine(), scope());
- transformer->createCommands(rtrafo->transform, engine());
- if (transformer->commands.isEmpty())
- throw Error(QString("There's a transformer without commands."), rtrafo->transform->location);
- }
-
- product->applyRules(artifactsPerFileTag);
-
- QSet<Artifact *> productArtifactCandidates;
- for (int i = 0; i < product->rProduct->fileTags.count(); ++i)
- foreach (Artifact *artifact, artifactsPerFileTag.value(product->rProduct->fileTags.at(i)))
- if (artifact->artifactType == Artifact::Generated)
- productArtifactCandidates += artifact;
-
- if (productArtifactCandidates.isEmpty()) {
- QString msg = QLatin1String("No artifacts generated for product '%1'.");
- throw Error(msg.arg(product->rProduct->name));
- }
-
- product->targetArtifacts += productArtifactCandidates;
- m_project->addBuildProduct(product);
- return product;
-}
-
-RulesEvaluationContext *BuildProjectResolver::evalContext() const
-{
- return m_project->evaluationContext();
-}
-
-ScriptEngine *BuildProjectResolver::engine() const
-{
- return evalContext()->engine();
-}
-
-QScriptValue BuildProjectResolver::scope() const
-{
- return evalContext()->scope();
-}
-
-BuildProjectLoader::LoadResult BuildProjectLoader::load(const QString &projectFilePath,
- RulesEvaluationContext *evalContext, const QString &buildRoot, const QVariantMap &cfg,
- const QStringList &loaderSearchPaths)
-{
- m_result = LoadResult();
- m_evalContext = evalContext;
-
- const QString projectId = ResolvedProject::deriveId(cfg);
- const QString buildDir = ResolvedProject::deriveBuildDirectory(buildRoot, projectId);
- const QString buildGraphFilePath = BuildProject::deriveBuildGraphFilePath(buildDir, projectId);
-
- PersistentPool pool;
- qbsDebug() << "[BG] trying to load: " << buildGraphFilePath;
- if (!pool.load(buildGraphFilePath))
- return m_result;
- if (!isConfigCompatible(cfg, pool.headData().projectConfig)) {
- qbsDebug() << "[BG] Cannot use stored build graph: Incompatible project configuration.";
- return m_result;
- }
-
- const BuildProjectPtr project = BuildProjectPtr(new BuildProject);
- project->setEvaluationContext(evalContext);
- TimedActivityLogger loadLogger(QLatin1String("Loading build graph"), QLatin1String("[BG] "));
- project->load(pool);
- foreach (const BuildProductPtr &bp, project->buildProducts())
- bp->project = project;
- project->resolvedProject()->location = CodeLocation(projectFilePath, 1, 1);
- project->resolvedProject()->setBuildConfiguration(pool.headData().projectConfig);
- project->resolvedProject()->buildDirectory = buildDir;
- m_result.loadedProject = project;
- loadLogger.finishActivity();
-
- const FileInfo bgfi(buildGraphFilePath);
- const bool projectFileChanged = bgfi.lastModified() < FileInfo(projectFilePath).lastModified();
-
- bool referencedProductRemoved = false;
- QList<BuildProductPtr> changedProducts;
- foreach (BuildProductPtr product, project->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 ResolvedGroup::Ptr &group, resolvedProduct->groups) {
- if (!group->wildcards)
- continue;
- const QSet<QString> files
- = group->wildcards->expandPatterns(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()) {
- Loader ldr(evalContext->engine());
- ldr.setSearchPaths(loaderSearchPaths);
- const ResolvedProjectPtr changedProject
- = ldr.loadProject(project->resolvedProject()->location.fileName, buildRoot, cfg);
- m_result.changedResolvedProject = changedProject;
-
- QMap<QString, ResolvedProductPtr> changedProductsMap;
- foreach (BuildProductPtr product, changedProducts) {
- if (changedProductsMap.isEmpty())
- foreach (ResolvedProductPtr cp, changedProject->products)
- changedProductsMap.insert(cp->name, cp);
- ResolvedProductPtr changedProduct = changedProductsMap.value(product->rProduct->name);
- if (!changedProduct)
- continue;
- onProductChanged(product, changedProduct);
- if (m_result.discardLoadedProject)
- return m_result;
- }
-
- QSet<QString> oldProductNames, newProductNames;
- foreach (const BuildProductPtr &product, project->buildProducts())
- oldProductNames += product->rProduct->name;
- foreach (const ResolvedProductConstPtr &product, changedProject->products)
- newProductNames += product->name;
- QSet<QString> addedProductNames = newProductNames - oldProductNames;
- if (!addedProductNames.isEmpty()) {
- m_result.discardLoadedProject = true;
- return m_result;
- }
- QSet<QString> removedProductsNames = oldProductNames - newProductNames;
- if (!removedProductsNames.isEmpty()) {
- foreach (const BuildProductPtr &product, project->buildProducts()) {
- if (removedProductsNames.contains(product->rProduct->name))
- onProductRemoved(product);
- }
- }
-
- CycleDetector().visitProject(project);
- }
-
- return m_result;
-}
-
-void BuildProjectLoader::onProductRemoved(const BuildProductPtr &product)
-{
- 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();
- BuildGraph::removeGeneratedArtifactFromDisk(artifact);
- }
-}
-
-void BuildProjectLoader::onProductChanged(const BuildProductPtr &product,
- const ResolvedProductPtr &changedProduct)
-{
- 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->allFiles();
- foreach (const SourceArtifactConstPtr &a, oldProductAllFiles)
- oldArtifacts.insert(a->absoluteFilePath, a);
- foreach (const SourceArtifactPtr &a, changedProduct->allFiles()) {
- newArtifacts.insert(a->absoluteFilePath, a);
- if (!oldArtifacts.contains(a->absoluteFilePath)) {
- // artifact added
- qbsDebug() << "[BG] artifact '" << a->absoluteFilePath << "' added to product " << product->rProduct->name;
- addedArtifacts += product->createArtifact(a);
- addedSourceArtifacts += a;
- }
- }
-
- foreach (const SourceArtifactPtr &a, oldProductAllFiles) {
- const SourceArtifactConstPtr changedArtifact = newArtifacts.value(a->absoluteFilePath);
- if (!changedArtifact) {
- // artifact removed
- qbsDebug() << "[BG] artifact '" << a->absoluteFilePath << "' removed from product " << product->rProduct->name;
- Artifact *artifact = product->lookupArtifact(a->absoluteFilePath);
- Q_ASSERT(artifact);
- removeArtifactAndExclusiveDependents(artifact, &artifactsToRemove);
- continue;
- }
- if (!addedSourceArtifacts.contains(changedArtifact)
- && changedArtifact->properties->value() != a->properties->value())
- {
- qbsInfo("Some properties changed. Regenerating build graph.");
- qbsDebug("Artifact with changed properties: %s", qPrintable(changedArtifact->absoluteFilePath));
- m_result.discardLoadedProject = true;
- return;
- }
- if (changedArtifact->fileTags != a->fileTags) {
- // artifact's filetags have changed
- qbsDebug() << "[BG] filetags have changed for artifact '" << a->absoluteFilePath
- << "' from " << a->fileTags << " to " << changedArtifact->fileTags;
- Artifact *artifact = product->lookupArtifact(a->absoluteFilePath);
- Q_ASSERT(artifact);
-
- // handle added filetags
- foreach (const QString &addedFileTag, changedArtifact->fileTags - a->fileTags)
- artifactsPerFileTag[addedFileTag] += artifact;
-
- // handle removed filetags
- foreach (const QString &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 QString &ft, artifact->fileTags)
- artifactsPerFileTag[ft] += artifact;
- product->applyRules(artifactsPerFileTag);
-
- // 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) {
- BuildGraph::removeGeneratedArtifactFromDisk(artifact);
- 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;
- BuildGraph::disconnect(parent, artifact);
- 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);
-}
-
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/buildgraph/buildgraph.h b/src/lib/buildgraph/buildgraph.h
index 25dd664d5..c8c7ec546 100644
--- a/src/lib/buildgraph/buildgraph.h
+++ b/src/lib/buildgraph/buildgraph.h
@@ -30,116 +30,15 @@
#ifndef QBS_BUILDGRAPH_H
#define QBS_BUILDGRAPH_H
-#include "artifactlist.h"
-#include "forward_decls.h"
-
#include <language/forward_decls.h>
-#include <tools/error.h>
-#include <tools/persistentobject.h>
-#include <tools/weakpointer.h>
-#include <QDir>
-#include <QScriptProgram>
#include <QScriptValue>
-#include <QSet>
#include <QStringList>
-#include <QVariant>
-#include <QVector>
namespace qbs {
namespace Internal {
-class ProgressObserver;
-class RulesEvaluationContext;
+class Artifact;
class ScriptEngine;
-class Transformer;
-
-typedef QMap<QString, ArtifactList> ArtifactsPerFileTagMap;
-
-class BuildProduct : public PersistentObject
-{
-public:
- static BuildProductPtr create() { return BuildProductPtr(new BuildProduct); }
-
- ~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 *createArtifact(const SourceArtifactConstPtr &sourceArtifact);
- void insertArtifact(Artifact *n);
- void applyRules(ArtifactsPerFileTagMap &artifactsPerFileTag);
-
- WeakPointer<BuildProject> project;
- ResolvedProductPtr rProduct;
- QSet<Artifact *> targetArtifacts;
- QList<BuildProductPtr> dependencies;
- ArtifactList artifacts;
-
-private:
- BuildProduct();
-
- void load(PersistentPool &pool);
- void store(PersistentPool &pool) const;
-
-private:
- mutable QList<RuleConstPtr> m_topSortedRules;
-};
-
-class BuildGraph;
-class BuildProjectLoader;
-class BuildProjectResolver;
-
-class BuildProject : public PersistentObject
-{
- friend class BuildProjectLoader;
- friend class BuildProjectResolver;
-public:
- BuildProject();
- ~BuildProject();
-
- void store() const;
- static QString deriveBuildGraphFilePath(const QString &buildDir, const QString projectId);
- QString buildGraphFilePath() const;
-
- void setEvaluationContext(RulesEvaluationContext *evalContext);
- RulesEvaluationContext *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;
- void insertFileDependency(Artifact *artifact);
- void rescueDependencies(const BuildProjectPtr &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:
- RulesEvaluationContext *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;
-};
/**
* N artifact, T transformer, parent -> child
@@ -160,98 +59,25 @@ public:
static bool safeConnect(Artifact *u, Artifact *v);
static void removeGeneratedArtifactFromDisk(Artifact *artifact);
static void disconnect(Artifact *u, Artifact *v);
+
static void setupScriptEngineForProduct(ScriptEngine *engine,
const ResolvedProductConstPtr &product, const RuleConstPtr &rule,
QScriptValue targetObject);
-private:
- BuildGraph();
-};
-
-class RulesApplicator
-{
-public:
- RulesApplicator(BuildProduct *product, ArtifactsPerFileTagMap &artifactsPerFileTag);
- void applyAllRules();
- void applyRule(const RuleConstPtr &rule);
-
-private:
- void doApply(const ArtifactList &inputArtifacts);
- void setupScriptEngineForArtifact(Artifact *artifact);
- Artifact *createOutputArtifact(const RuleArtifactConstPtr &ruleArtifact,
- const ArtifactList &inputArtifacts);
- QString resolveOutPath(const QString &path) const;
- RulesEvaluationContext *evalContext() const;
- ScriptEngine *engine() const;
- QScriptValue scope() const;
-
- BuildProduct * const m_buildProduct;
- ArtifactsPerFileTagMap &m_artifactsPerFileTag;
-
- RuleConstPtr m_rule;
- TransformerPtr m_transformer;
-};
-
-class BuildProjectResolver
-{
-public:
- BuildProjectPtr resolveProject(const ResolvedProjectPtr &resolvedProject,
- RulesEvaluationContext *evalContext);
-
-private:
- BuildProductPtr resolveProduct(const ResolvedProductPtr &rProduct);
-
- RulesEvaluationContext *evalContext() const;
- ScriptEngine *engine() const;
- QScriptValue scope() const;
-
- BuildProjectPtr m_project;
- QHash<ResolvedProductPtr, BuildProductPtr> m_productCache;
-};
-
-class BuildProjectLoader
-{
-public:
- struct LoadResult
+ static QString fileName(const Artifact *n); // Debugging helpers
+ template <typename T>
+ static QStringList toStringList(const T &artifactContainer)
{
- LoadResult() : discardLoadedProject(false) {}
-
- ResolvedProjectPtr changedResolvedProject;
- BuildProjectPtr loadedProject;
- bool discardLoadedProject;
- };
-
- LoadResult load(const QString &projectFilePath, RulesEvaluationContext *evalContext,
- const QString &buildRoot, const QVariantMap &cfg,
- const QStringList &loaderSearchPaths);
+ QStringList l;
+ foreach (Artifact *n, artifactContainer)
+ l.append(fileName(n));
+ return l;
+ }
private:
- void onProductRemoved(const BuildProductPtr &product);
- void onProductChanged(const BuildProductPtr &product,
- const ResolvedProductPtr &changedProduct);
- void removeArtifactAndExclusiveDependents(Artifact *artifact,
- QList<Artifact*> *removedArtifacts = 0);
-
- RulesEvaluationContext *m_evalContext;
- LoadResult m_result;
+ BuildGraph();
};
-// debugging helper
-QString fileName(Artifact *n);
-
-// debugging helper
-template <typename T>
-static QStringList toStringList(const T &artifactContainer)
-{
- QStringList l;
- foreach (Artifact *n, artifactContainer)
- l.append(fileName(n));
- return l;
-}
-
-char **createCFileTags(const QSet<QString> &fileTags);
-void freeCFileTags(char **cFileTags, int numFileTags);
-
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/buildgraph/buildgraph.pri b/src/lib/buildgraph/buildgraph.pri
index 45d34ffec..81942ab47 100644
--- a/src/lib/buildgraph/buildgraph.pri
+++ b/src/lib/buildgraph/buildgraph.pri
@@ -16,7 +16,10 @@ SOURCES += \
$$PWD/artifactvisitor.cpp \
$$PWD/artifactcleaner.cpp \
$$PWD/cycledetector.cpp \
- $$PWD/rulesevaluationcontext.cpp
+ $$PWD/rulesevaluationcontext.cpp \
+ $$PWD/buildproduct.cpp \
+ $$PWD/buildproject.cpp \
+ $$PWD/rulesapplicator.cpp
HEADERS += \
$$PWD/automoc.h\
@@ -37,4 +40,7 @@ HEADERS += \
$$PWD/artifactcleaner.h \
$$PWD/cycledetector.h \
$$PWD/forward_decls.h \
- $$PWD/rulesevaluationcontext.h
+ $$PWD/rulesevaluationcontext.h \
+ $$PWD/buildproduct.h \
+ $$PWD/buildproject.h \
+ $$PWD/rulesapplicator.h
diff --git a/src/lib/buildgraph/buildproduct.cpp b/src/lib/buildgraph/buildproduct.cpp
new file mode 100644
index 000000000..b3d796e4f
--- /dev/null
+++ b/src/lib/buildgraph/buildproduct.cpp
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "buildproduct.h"
+
+#include "artifact.h"
+#include "buildproject.h"
+#include "rulegraph.h"
+#include <language/language.h>
+#include <logging/logger.h>
+#include <tools/error.h>
+#include <tools/persistence.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(QStringList(artifact->fileTags.toList()).join(",")));
+ 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()) {
+ QStringList fileTags;
+ fileTags << rProduct->fileTags << rProduct->additionalFileTags;
+ fileTags.sort();
+ std::unique(fileTags.begin(), fileTags.end());
+ RuleGraph ruleGraph;
+ ruleGraph.build(rProduct->rules, fileTags);
+// 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::createArtifact(const SourceArtifactConstPtr &sourceArtifact)
+{
+ Artifact *artifact = new Artifact(project);
+ artifact->artifactType = Artifact::SourceFile;
+ artifact->setFilePath(sourceArtifact->absoluteFilePath);
+ artifact->fileTags = sourceArtifact->fileTags;
+ artifact->properties = sourceArtifact->properties;
+ insertArtifact(artifact);
+ return artifact;
+}
+
+void BuildProduct::insertArtifact(Artifact *artifact)
+{
+ Q_ASSERT(!artifact->product);
+ Q_ASSERT(!artifact->filePath().isEmpty());
+ Q_ASSERT(!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 (qbsLogLevel(LoggerTrace))
+ qbsTrace("[BG] insert artifact '%s'", qPrintable(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/buildproduct.h b/src/lib/buildgraph/buildproduct.h
new file mode 100644
index 000000000..b1cdbc2f1
--- /dev/null
+++ b/src/lib/buildgraph/buildproduct.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_BUILDPRODUCT_H
+#define QBS_BUILDPRODUCT_H
+
+#include "artifactlist.h"
+#include "forward_decls.h"
+#include <language/forward_decls.h>
+#include <tools/weakpointer.h>
+
+#include <tools/persistentobject.h>
+
+#include <QList>
+#include <QSet>
+
+namespace qbs {
+namespace Internal {
+
+class BuildProduct : public PersistentObject
+{
+public:
+ static BuildProductPtr create() { return BuildProductPtr(new BuildProduct); }
+
+ ~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 *createArtifact(const SourceArtifactConstPtr &sourceArtifact);
+ void insertArtifact(Artifact *n);
+
+ WeakPointer<BuildProject> project;
+ ResolvedProductPtr rProduct;
+ QSet<Artifact *> targetArtifacts;
+ QList<BuildProductPtr> dependencies;
+ ArtifactList artifacts;
+
+private:
+ BuildProduct();
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+
+private:
+ mutable QList<RuleConstPtr> m_topSortedRules;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_BUILDPRODUCT_H
diff --git a/src/lib/buildgraph/buildproject.cpp b/src/lib/buildgraph/buildproject.cpp
new file mode 100644
index 000000000..311149446
--- /dev/null
+++ b/src/lib/buildgraph/buildproject.cpp
@@ -0,0 +1,673 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "buildproject.h"
+
+#include "artifact.h"
+#include "buildgraph.h"
+#include "buildproduct.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/logger.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/persistence.h>
+
+namespace qbs {
+namespace Internal {
+
+BuildProject::BuildProject() : m_evalContext(0), m_dirty(false)
+{
+}
+
+BuildProject::~BuildProject()
+{
+ delete m_evalContext;
+ qDeleteAll(m_dependencyArtifacts);
+}
+
+void BuildProject::store() const
+{
+ if (!dirty()) {
+ qbsDebug() << "[BG] build graph is unchanged in project " << resolvedProject()->id() << ".";
+ return;
+ }
+ const QString fileName = buildGraphFilePath();
+ qbsDebug() << "[BG] storing: " << fileName;
+ PersistentPool pool;
+ 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(RulesEvaluationContext *evalContext)
+{
+ delete m_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;
+}
+
+void BuildProject::insertFileDependency(Artifact *artifact)
+{
+ Q_ASSERT(artifact->artifactType == Artifact::FileDependency);
+ m_dependencyArtifacts += artifact;
+ insertIntoArtifactLookupTable(artifact);
+}
+
+/**
+ * Copies dependencies between artifacts from the other project to this project.
+ */
+void BuildProject::rescueDependencies(const BuildProjectPtr &other)
+{
+ QHash<QString, BuildProductPtr> otherProductsByName;
+ foreach (const BuildProductPtr &product, other->m_buildProducts)
+ otherProductsByName.insert(product->rProduct->name, product);
+
+ foreach (const BuildProductPtr &product, m_buildProducts) {
+ BuildProductPtr otherProduct = otherProductsByName.value(product->rProduct->name);
+ if (!otherProduct)
+ continue;
+
+ if (qbsLogLevel(LoggerTrace))
+ qbsTrace("[BG] rescue dependencies of product '%s'", qPrintable(product->rProduct->name));
+
+ foreach (Artifact *artifact, product->artifacts) {
+ if (qbsLogLevel(LoggerTrace))
+ qbsTrace("[BG] artifact '%s'", qPrintable(artifact->fileName()));
+
+ Artifact *otherArtifact = otherProduct->lookupArtifact(artifact->dirPath(), artifact->fileName());
+ if (!otherArtifact || !otherArtifact->transformer)
+ continue;
+
+ foreach (Artifact *otherChild, otherArtifact->children) {
+ // skip transform edges
+ if (otherArtifact->transformer->inputs.contains(otherChild))
+ continue;
+
+ QList<Artifact *> children = lookupArtifacts(otherChild->dirPath(), otherChild->fileName());
+ foreach (Artifact *child, children) {
+ if (!artifact->children.contains(child))
+ BuildGraph::safeConnect(artifact, child);
+ }
+ }
+ }
+ }
+}
+
+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 (qbsLogLevel(LoggerTrace))
+ qbsTrace() << "[BG] remove artifact " << BuildGraph::fileName(artifact);
+
+ BuildGraph::removeGeneratedArtifactFromDisk(artifact);
+ 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());
+ foreach (Artifact *artifact, m_artifactsThatMustGetNewTransformers)
+ updateNodeThatMustGetNewTransformer(artifact);
+ m_artifactsThatMustGetNewTransformers.clear();
+}
+
+void BuildProject::updateNodeThatMustGetNewTransformer(Artifact *artifact)
+{
+ Q_ASSERT(artifact->transformer);
+
+ if (qbsLogLevel(LoggerDebug))
+ qbsDebug() << "[BG] updating transformer for " << BuildGraph::fileName(artifact);
+
+ BuildGraph::removeGeneratedArtifactFromDisk(artifact);
+
+ const RuleConstPtr rule = artifact->transformer->rule;
+ markDirty();
+ artifact->transformer = TransformerPtr();
+
+ ArtifactsPerFileTagMap artifactsPerFileTag;
+ foreach (Artifact *input, artifact->children) {
+ foreach (const QString &fileTag, input->fileTags)
+ artifactsPerFileTag[fileTag] += input;
+ }
+ RulesApplicator rulesApplier(artifact->product, artifactsPerFileTag);
+ rulesApplier.applyRule(rule);
+}
+
+void BuildProject::load(PersistentPool &pool)
+{
+ TimedActivityLogger 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);
+}
+
+
+BuildProjectPtr BuildProjectResolver::resolveProject(const ResolvedProjectPtr &resolvedProject,
+ RulesEvaluationContext *evalContext)
+{
+ m_productCache.clear();
+ m_project = BuildProjectPtr(new BuildProject);
+ m_project->setEvaluationContext(evalContext);
+ m_project->setResolvedProject(resolvedProject);
+ evalContext->initializeObserver(Tr::tr("Resolving project"), resolvedProject->products.count());
+ foreach (ResolvedProductPtr rProduct, resolvedProject->products) {
+ resolveProduct(rProduct);
+ evalContext->incrementProgressValue();
+ }
+ CycleDetector().visitProject(m_project);
+ return m_project;
+}
+
+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"));
+ 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);
+ }
+ qbsFileArtifact->fileTags.insert("qbs");
+ artifactsPerFileTag["qbs"].insert(qbsFileArtifact);
+
+ // read sources
+ foreach (const SourceArtifactConstPtr &sourceArtifact, rProduct->allFiles()) {
+ QString filePath = sourceArtifact->absoluteFilePath;
+ if (product->lookupArtifact(filePath))
+ continue; // ignore duplicate artifacts
+
+ Artifact *artifact = product->createArtifact(sourceArtifact);
+ foreach (const QString &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->inputs = rtrafo->inputs;
+ 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);
+ outputArtifact->artifactType = Artifact::Generated;
+ outputArtifact->transformer = transformer;
+ transformer->outputs += outputArtifact;
+ product->targetArtifacts += outputArtifact;
+ foreach (Artifact *inputArtifact, inputArtifacts)
+ BuildGraph::safeConnect(outputArtifact, inputArtifact);
+ foreach (const QString &fileTag, outputArtifact->fileTags)
+ artifactsPerFileTag[fileTag].insert(outputArtifact);
+
+ RuleArtifactPtr ruleArtifact = RuleArtifact::create();
+ ruleArtifact->fileName = outputArtifact->filePath();
+ ruleArtifact->fileTags = outputArtifact->fileTags.toList();
+ rule->artifacts += ruleArtifact;
+ }
+ transformer->rule = rule;
+
+ RulesEvaluationContext::Scope s(evalContext());
+ BuildGraph::setupScriptEngineForProduct(engine(), rProduct, transformer->rule, scope());
+ transformer->setupInputs(engine(), scope());
+ transformer->setupOutputs(engine(), scope());
+ transformer->createCommands(rtrafo->transform, engine());
+ if (transformer->commands.isEmpty())
+ throw Error(QString("There's a transformer without commands."), rtrafo->transform->location);
+ }
+
+ RulesApplicator(product.data(), artifactsPerFileTag).applyAllRules();
+
+ QSet<Artifact *> productArtifactCandidates;
+ for (int i = 0; i < product->rProduct->fileTags.count(); ++i)
+ foreach (Artifact *artifact, artifactsPerFileTag.value(product->rProduct->fileTags.at(i)))
+ if (artifact->artifactType == Artifact::Generated)
+ productArtifactCandidates += artifact;
+
+ if (productArtifactCandidates.isEmpty()) {
+ QString msg = QLatin1String("No artifacts generated for product '%1'.");
+ throw Error(msg.arg(product->rProduct->name));
+ }
+
+ product->targetArtifacts += productArtifactCandidates;
+ m_project->addBuildProduct(product);
+ return product;
+}
+
+RulesEvaluationContext *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::LoadResult BuildProjectLoader::load(const QString &projectFilePath,
+ RulesEvaluationContext *evalContext, const QString &buildRoot, const QVariantMap &cfg,
+ const QStringList &loaderSearchPaths)
+{
+ m_result = LoadResult();
+ m_evalContext = evalContext;
+
+ const QString projectId = ResolvedProject::deriveId(cfg);
+ const QString buildDir = ResolvedProject::deriveBuildDirectory(buildRoot, projectId);
+ const QString buildGraphFilePath = BuildProject::deriveBuildGraphFilePath(buildDir, projectId);
+
+ PersistentPool pool;
+ qbsDebug() << "[BG] trying to load: " << buildGraphFilePath;
+ if (!pool.load(buildGraphFilePath))
+ return m_result;
+ if (!isConfigCompatible(cfg, pool.headData().projectConfig)) {
+ qbsDebug() << "[BG] Cannot use stored build graph: Incompatible project configuration.";
+ return m_result;
+ }
+
+ const BuildProjectPtr project = BuildProjectPtr(new BuildProject);
+ project->setEvaluationContext(evalContext);
+ TimedActivityLogger loadLogger(QLatin1String("Loading build graph"), QLatin1String("[BG] "));
+ project->load(pool);
+ foreach (const BuildProductPtr &bp, project->buildProducts())
+ bp->project = project;
+ project->resolvedProject()->location = CodeLocation(projectFilePath, 1, 1);
+ project->resolvedProject()->setBuildConfiguration(pool.headData().projectConfig);
+ project->resolvedProject()->buildDirectory = buildDir;
+ m_result.loadedProject = project;
+ loadLogger.finishActivity();
+
+ const FileInfo bgfi(buildGraphFilePath);
+ const bool projectFileChanged = bgfi.lastModified() < FileInfo(projectFilePath).lastModified();
+
+ bool referencedProductRemoved = false;
+ QList<BuildProductPtr> changedProducts;
+ foreach (BuildProductPtr product, project->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 ResolvedGroup::Ptr &group, resolvedProduct->groups) {
+ if (!group->wildcards)
+ continue;
+ const QSet<QString> files
+ = group->wildcards->expandPatterns(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()) {
+ Loader ldr(evalContext->engine());
+ ldr.setSearchPaths(loaderSearchPaths);
+ const ResolvedProjectPtr changedProject
+ = ldr.loadProject(project->resolvedProject()->location.fileName, buildRoot, cfg);
+ m_result.changedResolvedProject = changedProject;
+
+ QMap<QString, ResolvedProductPtr> changedProductsMap;
+ foreach (BuildProductPtr product, changedProducts) {
+ if (changedProductsMap.isEmpty())
+ foreach (ResolvedProductPtr cp, changedProject->products)
+ changedProductsMap.insert(cp->name, cp);
+ ResolvedProductPtr changedProduct = changedProductsMap.value(product->rProduct->name);
+ if (!changedProduct)
+ continue;
+ onProductChanged(product, changedProduct);
+ if (m_result.discardLoadedProject)
+ return m_result;
+ }
+
+ QSet<QString> oldProductNames, newProductNames;
+ foreach (const BuildProductPtr &product, project->buildProducts())
+ oldProductNames += product->rProduct->name;
+ foreach (const ResolvedProductConstPtr &product, changedProject->products)
+ newProductNames += product->name;
+ QSet<QString> addedProductNames = newProductNames - oldProductNames;
+ if (!addedProductNames.isEmpty()) {
+ m_result.discardLoadedProject = true;
+ return m_result;
+ }
+ QSet<QString> removedProductsNames = oldProductNames - newProductNames;
+ if (!removedProductsNames.isEmpty()) {
+ foreach (const BuildProductPtr &product, project->buildProducts()) {
+ if (removedProductsNames.contains(product->rProduct->name))
+ onProductRemoved(product);
+ }
+ }
+
+ CycleDetector().visitProject(project);
+ }
+
+ return m_result;
+}
+
+void BuildProjectLoader::onProductRemoved(const BuildProductPtr &product)
+{
+ 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();
+ BuildGraph::removeGeneratedArtifactFromDisk(artifact);
+ }
+}
+
+void BuildProjectLoader::onProductChanged(const BuildProductPtr &product,
+ const ResolvedProductPtr &changedProduct)
+{
+ 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->allFiles();
+ foreach (const SourceArtifactConstPtr &a, oldProductAllFiles)
+ oldArtifacts.insert(a->absoluteFilePath, a);
+ foreach (const SourceArtifactPtr &a, changedProduct->allFiles()) {
+ newArtifacts.insert(a->absoluteFilePath, a);
+ if (!oldArtifacts.contains(a->absoluteFilePath)) {
+ // artifact added
+ qbsDebug() << "[BG] artifact '" << a->absoluteFilePath << "' added to product " << product->rProduct->name;
+ addedArtifacts += product->createArtifact(a);
+ addedSourceArtifacts += a;
+ }
+ }
+
+ foreach (const SourceArtifactPtr &a, oldProductAllFiles) {
+ const SourceArtifactConstPtr changedArtifact = newArtifacts.value(a->absoluteFilePath);
+ if (!changedArtifact) {
+ // artifact removed
+ qbsDebug() << "[BG] artifact '" << a->absoluteFilePath << "' removed from product " << product->rProduct->name;
+ Artifact *artifact = product->lookupArtifact(a->absoluteFilePath);
+ Q_ASSERT(artifact);
+ removeArtifactAndExclusiveDependents(artifact, &artifactsToRemove);
+ continue;
+ }
+ if (!addedSourceArtifacts.contains(changedArtifact)
+ && changedArtifact->properties->value() != a->properties->value())
+ {
+ qbsInfo("Some properties changed. Regenerating build graph.");
+ qbsDebug("Artifact with changed properties: %s", qPrintable(changedArtifact->absoluteFilePath));
+ m_result.discardLoadedProject = true;
+ return;
+ }
+ if (changedArtifact->fileTags != a->fileTags) {
+ // artifact's filetags have changed
+ qbsDebug() << "[BG] filetags have changed for artifact '" << a->absoluteFilePath
+ << "' from " << a->fileTags << " to " << changedArtifact->fileTags;
+ Artifact *artifact = product->lookupArtifact(a->absoluteFilePath);
+ Q_ASSERT(artifact);
+
+ // handle added filetags
+ foreach (const QString &addedFileTag, changedArtifact->fileTags - a->fileTags)
+ artifactsPerFileTag[addedFileTag] += artifact;
+
+ // handle removed filetags
+ foreach (const QString &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 QString &ft, artifact->fileTags)
+ artifactsPerFileTag[ft] += artifact;
+ RulesApplicator(product.data(), artifactsPerFileTag).applyAllRules();
+
+ // 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) {
+ BuildGraph::removeGeneratedArtifactFromDisk(artifact);
+ 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;
+ BuildGraph::disconnect(parent, artifact);
+ 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);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/buildgraph/buildproject.h b/src/lib/buildgraph/buildproject.h
new file mode 100644
index 000000000..6e40936ee
--- /dev/null
+++ b/src/lib/buildgraph/buildproject.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_BUILDPROJECT_H
+#define QBS_BUILDPROJECT_H
+
+#include "artifactlist.h"
+#include "forward_decls.h"
+#include <language/forward_decls.h>
+#include <tools/persistentobject.h>
+
+#include <QHash>
+#include <QList>
+#include <QScriptValue>
+#include <QSet>
+#include <QString>
+#include <QStringList>
+#include <QVariantMap>
+
+namespace qbs {
+namespace Internal {
+class BuildProjectLoader;
+class BuildProjectResolver;
+class RulesEvaluationContext;
+class ScriptEngine;
+
+class BuildProject : public PersistentObject
+{
+ friend class BuildProjectLoader;
+ friend class BuildProjectResolver;
+public:
+ BuildProject();
+ ~BuildProject();
+
+ void store() const;
+ static QString deriveBuildGraphFilePath(const QString &buildDir, const QString &projectId);
+ QString buildGraphFilePath() const;
+
+ void setEvaluationContext(RulesEvaluationContext *evalContext);
+ RulesEvaluationContext *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;
+ void insertFileDependency(Artifact *artifact);
+ void rescueDependencies(const BuildProjectPtr &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:
+ RulesEvaluationContext *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;
+};
+
+
+class BuildProjectResolver
+{
+public:
+ BuildProjectPtr resolveProject(const ResolvedProjectPtr &resolvedProject,
+ RulesEvaluationContext *evalContext);
+
+private:
+ BuildProductPtr resolveProduct(const ResolvedProductPtr &rProduct);
+
+ RulesEvaluationContext *evalContext() const;
+ ScriptEngine *engine() const;
+ QScriptValue scope() const;
+
+ BuildProjectPtr m_project;
+ QHash<ResolvedProductPtr, BuildProductPtr> m_productCache;
+};
+
+
+class BuildProjectLoader
+{
+public:
+ struct LoadResult
+ {
+ LoadResult() : discardLoadedProject(false) {}
+
+ ResolvedProjectPtr changedResolvedProject;
+ BuildProjectPtr loadedProject;
+ bool discardLoadedProject;
+ };
+
+ LoadResult load(const QString &projectFilePath, RulesEvaluationContext *evalContext,
+ const QString &buildRoot, const QVariantMap &cfg,
+ const QStringList &loaderSearchPaths);
+
+private:
+ void onProductRemoved(const BuildProductPtr &product);
+ void onProductChanged(const BuildProductPtr &product,
+ const ResolvedProductPtr &changedProduct);
+ void removeArtifactAndExclusiveDependents(Artifact *artifact,
+ QList<Artifact*> *removedArtifacts = 0);
+
+ RulesEvaluationContext *m_evalContext;
+ LoadResult m_result;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_BUILDPROJECT_H
diff --git a/src/lib/buildgraph/cycledetector.cpp b/src/lib/buildgraph/cycledetector.cpp
index fc9380709..501158bc0 100644
--- a/src/lib/buildgraph/cycledetector.cpp
+++ b/src/lib/buildgraph/cycledetector.cpp
@@ -30,10 +30,12 @@
#include "artifact.h"
#include "buildgraph.h"
+#include "buildproject.h"
#include <language/language.h>
#include <logging/logger.h>
#include <logging/translator.h>
+#include <tools/error.h>
namespace qbs {
namespace Internal {
diff --git a/src/lib/buildgraph/executor.cpp b/src/lib/buildgraph/executor.cpp
index df63aa111..fa4d6a8c5 100644
--- a/src/lib/buildgraph/executor.cpp
+++ b/src/lib/buildgraph/executor.cpp
@@ -31,6 +31,8 @@
#include "artifactvisitor.h"
#include "automoc.h"
#include "buildgraph.h"
+#include "buildproduct.h"
+#include "buildproject.h"
#include "cycledetector.h"
#include "executorjob.h"
#include "inputartifactscanner.h"
@@ -44,6 +46,7 @@
#include <tools/fileinfo.h>
#include <tools/progressobserver.h>
+#include <QDir>
#include <QSet>
#include <QTimer>
@@ -317,7 +320,7 @@ void Executor::buildArtifact(Artifact *artifact)
Q_ASSERT(!m_availableJobs.isEmpty());
if (qbsLogLevel(LoggerDebug))
- qbsDebug() << "[EXEC] " << fileName(artifact);
+ qbsDebug() << "[EXEC] " << BuildGraph::fileName(artifact);
// Skip artifacts that are already built.
if (artifact->buildState == Artifact::Built) {
@@ -475,23 +478,23 @@ void Executor::finishArtifact(Artifact *leaf)
Q_ASSERT(leaf);
if (qbsLogLevel(LoggerTrace))
- qbsTrace() << "[EXEC] finishArtifact " << fileName(leaf);
+ qbsTrace() << "[EXEC] finishArtifact " << BuildGraph::fileName(leaf);
leaf->buildState = Artifact::Built;
foreach (Artifact *parent, leaf->parents) {
if (parent->buildState != Artifact::Buildable) {
if (qbsLogLevel(LoggerTrace))
- qbsTrace() << "[EXEC] parent " << fileName(parent) << " build state: " << toString(parent->buildState);
+ qbsTrace() << "[EXEC] parent " << BuildGraph::fileName(parent) << " build state: " << toString(parent->buildState);
continue;
}
if (allChildrenBuilt(parent)) {
m_leaves.append(parent);
if (qbsLogLevel(LoggerTrace))
- qbsTrace() << "[EXEC] finishArtifact adds leaf " << fileName(parent) << " " << toString(parent->buildState);
+ qbsTrace() << "[EXEC] finishArtifact adds leaf " << BuildGraph::fileName(parent) << " " << toString(parent->buildState);
} else {
if (qbsLogLevel(LoggerTrace))
- qbsTrace() << "[EXEC] parent " << fileName(parent) << " build state: " << toString(parent->buildState);
+ qbsTrace() << "[EXEC] parent " << BuildGraph::fileName(parent) << " build state: " << toString(parent->buildState);
}
}
@@ -525,7 +528,7 @@ static void insertLeavesAfterAddingDependencies_recurse(Artifact *const artifact
if (isLeaf) {
if (qbsLogLevel(LoggerDebug))
- qbsDebug() << "[EXEC] adding leaf " << fileName(artifact);
+ qbsDebug() << "[EXEC] adding leaf " << BuildGraph::fileName(artifact);
leaves->append(artifact);
}
}
diff --git a/src/lib/buildgraph/executorjob.cpp b/src/lib/buildgraph/executorjob.cpp
index a99667d3d..ef4f99206 100644
--- a/src/lib/buildgraph/executorjob.cpp
+++ b/src/lib/buildgraph/executorjob.cpp
@@ -29,7 +29,7 @@
#include "executorjob.h"
-#include "buildgraph.h"
+#include "buildproduct.h"
#include "command.h"
#include "jscommandexecutor.h"
#include "processcommandexecutor.h"
diff --git a/src/lib/buildgraph/inputartifactscanner.cpp b/src/lib/buildgraph/inputartifactscanner.cpp
index bbe3f778a..60d5bf039 100644
--- a/src/lib/buildgraph/inputartifactscanner.cpp
+++ b/src/lib/buildgraph/inputartifactscanner.cpp
@@ -31,6 +31,8 @@
#include "artifact.h"
#include "buildgraph.h"
+#include "buildproduct.h"
+#include "buildproject.h"
#include "transformer.h"
#include <language/language.h>
@@ -38,6 +40,7 @@
#include <tools/fileinfo.h>
#include <tools/scannerpluginmanager.h>
+#include <QDir>
#include <QSet>
#include <QStringList>
#include <QVariantMap>
diff --git a/src/lib/buildgraph/jscommandexecutor.cpp b/src/lib/buildgraph/jscommandexecutor.cpp
index ca62ffb1d..16be8521d 100644
--- a/src/lib/buildgraph/jscommandexecutor.cpp
+++ b/src/lib/buildgraph/jscommandexecutor.cpp
@@ -31,6 +31,7 @@
#include "artifact.h"
#include "buildgraph.h"
+#include "buildproduct.h"
#include "command.h"
#include "transformer.h"
diff --git a/src/lib/buildgraph/processcommandexecutor.cpp b/src/lib/buildgraph/processcommandexecutor.cpp
index 333c9c151..4f6e0828c 100644
--- a/src/lib/buildgraph/processcommandexecutor.cpp
+++ b/src/lib/buildgraph/processcommandexecutor.cpp
@@ -30,7 +30,7 @@
#include "processcommandexecutor.h"
#include "artifact.h"
-#include "buildgraph.h"
+#include "buildproduct.h"
#include "command.h"
#include "transformer.h"
diff --git a/src/lib/buildgraph/rulesapplicator.cpp b/src/lib/buildgraph/rulesapplicator.cpp
new file mode 100644
index 000000000..bd19b1242
--- /dev/null
+++ b/src/lib/buildgraph/rulesapplicator.cpp
@@ -0,0 +1,310 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#include "rulesapplicator.h"
+
+#include "artifact.h"
+#include "buildgraph.h"
+#include "buildproduct.h"
+#include "buildproject.h"
+#include "rulesevaluationcontext.h"
+#include "transformer.h"
+#include <language/language.h>
+#include <language/scriptengine.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/scripttools.h>
+
+#include <QDir>
+
+namespace qbs {
+namespace Internal {
+
+RulesApplicator::RulesApplicator(BuildProduct *product, ArtifactsPerFileTagMap &artifactsPerFileTag)
+ : m_buildProduct(product)
+ , m_artifactsPerFileTag(artifactsPerFileTag)
+{
+}
+
+void RulesApplicator::applyAllRules()
+{
+ RulesEvaluationContext::Scope s(m_buildProduct->project->evaluationContext());
+ foreach (const RuleConstPtr &rule, m_buildProduct->topSortedRules())
+ applyRule(rule);
+}
+
+void RulesApplicator::applyRule(const RuleConstPtr &rule)
+{
+ m_rule = rule;
+ BuildGraph::setupScriptEngineForProduct(engine(), m_buildProduct->rProduct, m_rule, scope());
+ Q_ASSERT_X(scope().property("product").strictlyEquals(engine()->evaluate("product")),
+ "BG", "Product object is not in current scope.");
+
+ ArtifactList inputArtifacts;
+ foreach (const QString &fileTag, m_rule->inputs)
+ inputArtifacts.unite(m_artifactsPerFileTag.value(fileTag));
+ if (m_rule->multiplex) { // apply the rule once for a set of inputs
+ if (!inputArtifacts.isEmpty())
+ doApply(inputArtifacts);
+ } else { // apply the rule once for each input
+ ArtifactList lst;
+ foreach (Artifact * const inputArtifact, inputArtifacts) {
+ setupScriptEngineForArtifact(inputArtifact);
+ lst += inputArtifact;
+ doApply(lst);
+ lst.clear();
+ }
+ }
+}
+
+void RulesApplicator::doApply(const ArtifactList &inputArtifacts)
+{
+ evalContext()->checkForCancelation();
+
+ if (qbsLogLevel(LoggerDebug))
+ qbsDebug() << "[BG] apply rule " << m_rule->toString() << " "
+ << BuildGraph::toStringList(inputArtifacts).join(",\n ");
+
+ QList<QPair<const RuleArtifact *, Artifact *> > ruleArtifactArtifactMap;
+ QList<Artifact *> outputArtifacts;
+
+ ArtifactList usingArtifacts;
+ if (!m_rule->usings.isEmpty()) {
+ const QSet<QString> usingsFileTags = m_rule->usings.toSet();
+ foreach (const BuildProductPtr &dep, m_buildProduct->dependencies) {
+ ArtifactList artifactsToCheck;
+ foreach (Artifact *targetArtifact, dep->targetArtifacts)
+ artifactsToCheck.unite(targetArtifact->transformer->outputs);
+ foreach (Artifact *artifact, artifactsToCheck) {
+ QSet<QString> matchingFileTags = artifact->fileTags;
+ matchingFileTags.intersect(usingsFileTags);
+ if (!matchingFileTags.isEmpty())
+ usingArtifacts.insert(artifact);
+ }
+ }
+ }
+
+ m_transformer.clear();
+ // create the output artifacts from the set of input artifacts
+ foreach (const RuleArtifactConstPtr &ruleArtifact, m_rule->artifacts) {
+ Artifact * const outputArtifact = createOutputArtifact(ruleArtifact, inputArtifacts);
+ outputArtifacts << outputArtifact;
+ ruleArtifactArtifactMap << qMakePair(ruleArtifact.data(), outputArtifact);
+ }
+
+ foreach (Artifact *outputArtifact, outputArtifacts) {
+ // insert the output artifacts into the pool of artifacts
+ foreach (const QString &fileTag, outputArtifact->fileTags)
+ m_artifactsPerFileTag[fileTag].insert(outputArtifact);
+
+ // connect artifacts that match the file tags in explicitlyDependsOn
+ foreach (const QString &fileTag, m_rule->explicitlyDependsOn)
+ foreach (Artifact *dependency, m_artifactsPerFileTag.value(fileTag))
+ BuildGraph::loggedConnect(outputArtifact, dependency);
+
+ // Transformer setup
+ for (ArtifactList::const_iterator it = usingArtifacts.constBegin();
+ it != usingArtifacts.constEnd(); ++it)
+ {
+ Artifact *dep = *it;
+ BuildGraph::loggedConnect(outputArtifact, dep);
+ m_transformer->inputs.insert(dep);
+ }
+ m_transformer->outputs.insert(outputArtifact);
+
+ m_buildProduct->project->removeFromArtifactsThatMustGetNewTransformers(outputArtifact);
+ }
+
+ m_transformer->setupInputs(engine(), scope());
+
+ // change the transformer outputs according to the bindings in Artifact
+ QScriptValue scriptValue;
+ for (int i = ruleArtifactArtifactMap.count(); --i >= 0;) {
+ const RuleArtifact *ra = ruleArtifactArtifactMap.at(i).first;
+ if (ra->bindings.isEmpty())
+ continue;
+
+ // expose attributes of this artifact
+ Artifact *outputArtifact = ruleArtifactArtifactMap.at(i).second;
+ outputArtifact->properties = outputArtifact->properties->clone();
+
+ scope().setProperty("fileName", engine()->toScriptValue(outputArtifact->filePath()));
+ scope().setProperty("fileTags", toScriptValue(engine(), outputArtifact->fileTags));
+
+ QVariantMap artifactModulesCfg = outputArtifact->properties->value().value("modules").toMap();
+ for (int i=0; i < ra->bindings.count(); ++i) {
+ const RuleArtifact::Binding &binding = ra->bindings.at(i);
+ scriptValue = engine()->evaluate(binding.code);
+ if (scriptValue.isError()) {
+ QString msg = QLatin1String("evaluating rule binding '%1': %2");
+ throw Error(msg.arg(binding.name.join(QLatin1String(".")), scriptValue.toString()), binding.location);
+ }
+ setConfigProperty(artifactModulesCfg, binding.name, scriptValue.toVariant());
+ }
+ QVariantMap outputArtifactConfig = outputArtifact->properties->value();
+ outputArtifactConfig.insert("modules", artifactModulesCfg);
+ outputArtifact->properties->setValue(outputArtifactConfig);
+ }
+
+ m_transformer->setupOutputs(engine(), scope());
+ m_transformer->createCommands(m_rule->script, engine());
+ if (m_transformer->commands.isEmpty())
+ throw Error(QString("There's a rule without commands: %1.").arg(m_rule->toString()), m_rule->script->location);
+}
+
+void RulesApplicator::setupScriptEngineForArtifact(Artifact *artifact)
+{
+ QString inFileName = artifact->fileName();
+ QString inBaseName = FileInfo::baseName(artifact->filePath());
+ QString inCompleteBaseName = FileInfo::completeBaseName(artifact->filePath());
+
+ QString basedir;
+ if (artifact->artifactType == Artifact::SourceFile) {
+ QDir sourceDir(m_buildProduct->rProduct->sourceDirectory);
+ basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->filePath()));
+ } else {
+ QDir buildDir(m_buildProduct->project->resolvedProject()->buildDirectory);
+ basedir = FileInfo::path(buildDir.relativeFilePath(artifact->filePath()));
+ }
+
+ QScriptValue modulesScriptValue = artifact->properties->toScriptValue(engine());
+ modulesScriptValue = modulesScriptValue.property("modules");
+
+ // expose per file properties we want to use in an Artifact within a Rule
+ QScriptValue scriptValue = engine()->newObject();
+ scriptValue.setProperty("fileName", inFileName);
+ scriptValue.setProperty("baseName", inBaseName);
+ scriptValue.setProperty("completeBaseName", inCompleteBaseName);
+ scriptValue.setProperty("baseDir", basedir);
+ scriptValue.setProperty("modules", modulesScriptValue);
+
+ scope().setProperty("input", scriptValue);
+ Q_ASSERT_X(scriptValue.strictlyEquals(engine()->evaluate("input")),
+ "BG", "The input object is not in current scope.");
+}
+
+Artifact *RulesApplicator::createOutputArtifact(const RuleArtifactConstPtr &ruleArtifact,
+ const ArtifactList &inputArtifacts)
+{
+ QScriptValue scriptValue = engine()->evaluate(ruleArtifact->fileName);
+ if (scriptValue.isError() || engine()->hasUncaughtException())
+ throw Error("Error in Rule.Artifact fileName: " + scriptValue.toString());
+ QString outputPath = scriptValue.toString();
+ outputPath.replace("..", "dotdot"); // don't let the output artifact "escape" its build dir
+ outputPath = resolveOutPath(outputPath);
+
+ Artifact *outputArtifact = m_buildProduct->lookupArtifact(outputPath);
+ if (outputArtifact) {
+ if (outputArtifact->transformer && outputArtifact->transformer != m_transformer) {
+ // This can happen when applying rules after scanning for additional file tags.
+ // We just regenerate the transformer.
+ if (qbsLogLevel(LoggerTrace))
+ qbsTrace("[BG] regenerating transformer for '%s'", qPrintable(BuildGraph::fileName(outputArtifact)));
+ m_transformer = outputArtifact->transformer;
+ m_transformer->inputs.unite(inputArtifacts);
+
+ if (m_transformer->inputs.count() > 1 && !m_rule->multiplex) {
+ QString th = "[" + QStringList(outputArtifact->fileTags.toList()).join(", ") + "]";
+ QString e = Tr::tr("Conflicting rules for producing %1 %2 \n").arg(outputArtifact->filePath(), th);
+ th = "[" + m_rule->inputs.join(", ")
+ + "] -> [" + QStringList(outputArtifact->fileTags.toList()).join(", ") + "]";
+
+ e += QString(" while trying to apply: %1:%2:%3 %4\n")
+ .arg(m_rule->script->location.fileName)
+ .arg(m_rule->script->location.line)
+ .arg(m_rule->script->location.column)
+ .arg(th);
+
+ e += QString(" was already defined in: %1:%2:%3 %4\n")
+ .arg(outputArtifact->transformer->rule->script->location.fileName)
+ .arg(outputArtifact->transformer->rule->script->location.line)
+ .arg(outputArtifact->transformer->rule->script->location.column)
+ .arg(th);
+ throw Error(e);
+ }
+ }
+ outputArtifact->fileTags += ruleArtifact->fileTags.toSet();
+ } else {
+ outputArtifact = new Artifact(m_buildProduct->project);
+ outputArtifact->artifactType = Artifact::Generated;
+ outputArtifact->setFilePath(outputPath);
+ outputArtifact->fileTags = ruleArtifact->fileTags.toSet();
+ outputArtifact->alwaysUpdated = ruleArtifact->alwaysUpdated;
+ m_buildProduct->insertArtifact(outputArtifact);
+ }
+
+ if (outputArtifact->fileTags.isEmpty())
+ outputArtifact->fileTags = m_buildProduct->rProduct->fileTagsForFileName(outputArtifact->fileName());
+
+ if (m_rule->multiplex)
+ outputArtifact->properties = m_buildProduct->rProduct->properties;
+ else
+ outputArtifact->properties= (*inputArtifacts.constBegin())->properties;
+
+ foreach (Artifact *inputArtifact, inputArtifacts) {
+ Q_ASSERT(outputArtifact != inputArtifact);
+ BuildGraph::loggedConnect(outputArtifact, inputArtifact);
+ }
+
+ // create transformer if not already done so
+ if (!m_transformer) {
+ m_transformer = Transformer::create();
+ m_transformer->rule = m_rule;
+ m_transformer->inputs = inputArtifacts;
+ }
+ outputArtifact->transformer = m_transformer;
+
+ return outputArtifact;
+}
+
+QString RulesApplicator::resolveOutPath(const QString &path) const
+{
+ QString buildDir = m_buildProduct->project->resolvedProject()->buildDirectory;
+ QString result = FileInfo::resolvePath(buildDir, path);
+ result = QDir::cleanPath(result);
+ return result;
+}
+
+RulesEvaluationContext *RulesApplicator::evalContext() const
+{
+ return m_buildProduct->project->evaluationContext();
+}
+
+ScriptEngine *RulesApplicator::engine() const
+{
+ return evalContext()->engine();
+}
+
+QScriptValue RulesApplicator::scope() const
+{
+ return evalContext()->scope();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/buildgraph/rulesapplicator.h b/src/lib/buildgraph/rulesapplicator.h
new file mode 100644
index 000000000..405353c10
--- /dev/null
+++ b/src/lib/buildgraph/rulesapplicator.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+#ifndef QBS_RULESAPPLICATOR_H
+#define QBS_RULESAPPLICATOR_H
+
+#include "artifactlist.h"
+#include "forward_decls.h"
+#include <language/forward_decls.h>
+
+#include <QMap>
+#include <QScriptValue>
+#include <QString>
+
+namespace qbs {
+namespace Internal {
+class RulesEvaluationContext;
+class ScriptEngine;
+
+typedef QMap<QString, ArtifactList> ArtifactsPerFileTagMap;
+
+class RulesApplicator
+{
+public:
+ RulesApplicator(BuildProduct *product, ArtifactsPerFileTagMap &artifactsPerFileTag);
+ void applyAllRules();
+ void applyRule(const RuleConstPtr &rule);
+
+private:
+ void doApply(const ArtifactList &inputArtifacts);
+ void setupScriptEngineForArtifact(Artifact *artifact);
+ Artifact *createOutputArtifact(const RuleArtifactConstPtr &ruleArtifact,
+ const ArtifactList &inputArtifacts);
+ QString resolveOutPath(const QString &path) const;
+ RulesEvaluationContext *evalContext() const;
+ ScriptEngine *engine() const;
+ QScriptValue scope() const;
+
+ BuildProduct * const m_buildProduct;
+ ArtifactsPerFileTagMap &m_artifactsPerFileTag;
+
+ RuleConstPtr m_rule;
+ TransformerPtr m_transformer;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_RULESAPPLICATOR_H
diff --git a/src/lib/buildgraph/rulesevaluationcontext.cpp b/src/lib/buildgraph/rulesevaluationcontext.cpp
index 9c5db3643..d0b41bdef 100644
--- a/src/lib/buildgraph/rulesevaluationcontext.cpp
+++ b/src/lib/buildgraph/rulesevaluationcontext.cpp
@@ -29,7 +29,6 @@
#include "rulesevaluationcontext.h"
#include "artifact.h"
-#include "buildgraph.h"
#include "command.h"
#include "transformer.h"
#include <language/language.h>
diff --git a/src/lib/buildgraph/transformer.cpp b/src/lib/buildgraph/transformer.cpp
index cabc40d9c..eff5c1f60 100644
--- a/src/lib/buildgraph/transformer.cpp
+++ b/src/lib/buildgraph/transformer.cpp
@@ -33,6 +33,7 @@
#include <language/language.h>
#include <language/scriptengine.h>
#include <tools/error.h>
+#include <tools/persistence.h>
namespace qbs {
namespace Internal {
@@ -155,5 +156,33 @@ void Transformer::createCommands(const PrepareScriptConstPtr &script, ScriptEngi
}
}
+void Transformer::load(PersistentPool &pool)
+{
+ rule = pool.idLoadS<Rule>();
+ pool.loadContainer(inputs);
+ pool.loadContainer(outputs);
+ int count, cmdType;
+ pool.stream() >> count;
+ commands.reserve(count);
+ while (--count >= 0) {
+ pool.stream() >> cmdType;
+ AbstractCommand *cmd = AbstractCommand::createByType(static_cast<AbstractCommand::CommandType>(cmdType));
+ cmd->load(pool.stream());
+ commands += cmd;
+ }
+}
+
+void Transformer::store(PersistentPool &pool) const
+{
+ pool.store(rule);
+ pool.storeContainer(inputs);
+ pool.storeContainer(outputs);
+ pool.stream() << commands.count();
+ foreach (AbstractCommand *cmd, commands) {
+ pool.stream() << int(cmd->type());
+ cmd->store(pool.stream());
+ }
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/tests/auto/buildgraph/tst_buildgraph.cpp b/tests/auto/buildgraph/tst_buildgraph.cpp
index cf8fb479b..da0e6f4f7 100644
--- a/tests/auto/buildgraph/tst_buildgraph.cpp
+++ b/tests/auto/buildgraph/tst_buildgraph.cpp
@@ -29,7 +29,7 @@
#include "tst_buildgraph.h"
#include <buildgraph/artifact.h>
-#include <buildgraph/buildgraph.h>
+#include <buildgraph/buildproduct.h>
#include <buildgraph/cycledetector.h>
#include <language/language.h>
#include <tools/error.h>