summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoerg Bornemann <joerg.bornemann@digia.com>2013-08-02 13:57:26 +0200
committerJoerg Bornemann <joerg.bornemann@digia.com>2013-11-27 11:49:01 +0100
commit006824f89c4f298f19c45f4f4585b4452cd2d2b9 (patch)
tree0007c73137e7d167cefa4e5611a01803e69fa5fd
parentdbb39fc1fce3686a5161d68e89e96086fc664556 (diff)
downloadqbs-dynablaster.tar.gz
introduce class BuildGraphNodedynablaster
To support different types of nodes in the build graph, we introduce the base class BuildGraphNode. Artifact now derives from BuildGraphNode. A - yet unused - RuleNode class is introduced that represents a rule in the build graph. Change-Id: Ie1ad5e7a9e65bfd5f99d3e824737516053de0f94 ***new artifacts hash in ProductBuildData Change-Id: I50521ec64f734bfb3662759076fa5e7912ac084e ***automoc fixup Change-Id: Ie2bb228ae38d63deccc600d67d283bc3beda9fec slightly nicer rule debug output Change-Id: I60ddef73cd7f0a67055f2d56e6dbe7fa45ddd450 create RuleNode objects and add them to roots Change-Id: I51a0f89b98cda524d34888ab758d7bd397d82f00 ***execute rule nodes on build Change-Id: I291c32868cd982e37393d6bc8d696f2b63fbff50 ***ArtifactSet::fromNodeSet Change-Id: I7586bcfbe43406c22b1ee1a1fd826beaa247164f ***update after rebase Change-Id: I5f73afc80b746b11eeb83aeadf29c2358accbab8 ***phonytarget Change-Id: I587b7e072e54b866e8d5ea37c874e6a8baa550e0 update after rebase Change-Id: I6017c0cbe9244fea04d513f53e3a2a85092bf962 skip nodes early We must skip Building and Built nodes. m_leaves can contain duplicates now. Change-Id: If275106a47c403a455103f4deeb20049548007f5 debug message Change-Id: I90e238eef72a8a02fb0a9f2c77715f5f15c1442d disable phony thingy Change-Id: I7bb60b850b96b65ab738c11e96c3a84cbe58d2f7 connect each output of a rule to the rule itself Change-Id: I159d76c4b63094058cc9a07ccc2d0b67c7ce06e6 TagScanner Change-Id: I4741e48cd674c0ca830ee1b4d05b59a8c542694f TagScanner deps Change-Id: If3e43a7ac25616cd151229684a085ac8ee787645 update after rebase Change-Id: I58d49a5ed1049a17733fc2154588f12c1cda53e6 only root tag scanners in product Change-Id: Ia94732a5632156a5ec3c5518c9596cd4cefac105 run TagScanners Change-Id: I869a183f2da23a43957a6df45dc5a1b30b10af92 TagScannerRunner Change-Id: Id3c5fac73e6fb84e56d6ab1d48a959f3677f7a67 crash fix Change-Id: Ie3c26dbed41ab4615915a08913c495868c3a8910 more TagScanRunner Change-Id: I80b79f101c0f4d802fb4f6515c0848e369fed066 handle removal of filetags Change-Id: Icdd8d05c2e26953f7b12d968aa5dfcc23928b9ba Qt4 build fix Change-Id: Ie5fcb64e36ddc5004003183bea7243536bc8ab15 do not waste space with type information Change-Id: I7b9354e6fceead228ba335723b9addeb701bef56 update nodes that must get new transformers Change-Id: I06cd7ffe606ae643393350c5959ef6475343929f update after rebase Change-Id: If080e664972f2950c12fd800a3e4b521e65dca3a only run tag scanners on changed sources And do not call RulesApplicator in bgloader. Change-Id: I5140c91c79a842dab933ad7b328100bdec13cb10 Conflicts: src/lib/buildgraph/buildgraphloader.cpp sketch of moc tagscanners impl Change-Id: I7736c2da1b1742bc3556ef660c163e6c32cbd4c9 Conflicts: share/qbs/modules/Qt/core/core.qbs MocTagScanner Change-Id: I87199e8f355b8b20cf2387f4d4190a20bddae0db fix lib.qbs Change-Id: Ibcb5bc290d3839b04828f8c9b155577634bcc9e4 moctagscanner split Change-Id: Iace5d48483c02b5d33d9ccd54ac07162bfc09d27
-rw-r--r--share/qbs/modules/Qt/core/core.qbs56
-rw-r--r--src/lib/api/project.cpp31
-rw-r--r--src/lib/buildgraph/artifact.cpp33
-rw-r--r--src/lib/buildgraph/artifact.h29
-rw-r--r--src/lib/buildgraph/artifactcleaner.cpp2
-rw-r--r--src/lib/buildgraph/artifactset.cpp49
-rw-r--r--src/lib/buildgraph/artifactset.h (renamed from src/lib/buildgraph/artifactlist.cpp)32
-rw-r--r--src/lib/buildgraph/artifactvisitor.cpp35
-rw-r--r--src/lib/buildgraph/artifactvisitor.h9
-rw-r--r--src/lib/buildgraph/automoc.cpp344
-rw-r--r--src/lib/buildgraph/buildgraph.cpp45
-rw-r--r--src/lib/buildgraph/buildgraph.h21
-rw-r--r--src/lib/buildgraph/buildgraph.pri23
-rw-r--r--src/lib/buildgraph/buildgraphloader.cpp74
-rw-r--r--src/lib/buildgraph/buildgraphloader.h7
-rw-r--r--src/lib/buildgraph/buildgraphnode.cpp79
-rw-r--r--src/lib/buildgraph/buildgraphnode.h86
-rw-r--r--src/lib/buildgraph/buildgraphvisitor.h57
-rw-r--r--src/lib/buildgraph/cycledetector.cpp32
-rw-r--r--src/lib/buildgraph/cycledetector.h15
-rw-r--r--src/lib/buildgraph/executor.cpp351
-rw-r--r--src/lib/buildgraph/executor.h39
-rw-r--r--src/lib/buildgraph/inputartifactscanner.cpp5
-rw-r--r--src/lib/buildgraph/moctagscanner.cpp194
-rw-r--r--src/lib/buildgraph/moctagscanner.h (renamed from src/lib/buildgraph/automoc.h)41
-rw-r--r--src/lib/buildgraph/nodeset.cpp101
-rw-r--r--src/lib/buildgraph/nodeset.h (renamed from src/lib/buildgraph/artifactlist.h)48
-rw-r--r--src/lib/buildgraph/phonytarget.cpp83
-rw-r--r--src/lib/buildgraph/phonytarget.h67
-rw-r--r--src/lib/buildgraph/productbuilddata.cpp40
-rw-r--r--src/lib/buildgraph/productbuilddata.h29
-rw-r--r--src/lib/buildgraph/productinstaller.cpp3
-rw-r--r--src/lib/buildgraph/projectbuilddata.cpp91
-rw-r--r--src/lib/buildgraph/projectbuilddata.h10
-rw-r--r--src/lib/buildgraph/rulegraph.cpp15
-rw-r--r--src/lib/buildgraph/rulegraph.h9
-rw-r--r--src/lib/buildgraph/rulenode.cpp167
-rw-r--r--src/lib/buildgraph/rulenode.h80
-rw-r--r--src/lib/buildgraph/rulesapplicator.cpp35
-rw-r--r--src/lib/buildgraph/rulesapplicator.h14
-rw-r--r--src/lib/buildgraph/tagscannerrunner.cpp208
-rw-r--r--src/lib/buildgraph/tagscannerrunner.h82
-rw-r--r--src/lib/buildgraph/transformer.cpp5
-rw-r--r--src/lib/buildgraph/transformer.h8
-rw-r--r--src/lib/language/builtindeclarations.cpp17
-rw-r--r--src/lib/language/builtindeclarations.h1
-rw-r--r--src/lib/language/forward_decls.h4
-rw-r--r--src/lib/language/language.cpp73
-rw-r--r--src/lib/language/language.h47
-rw-r--r--src/lib/language/projectresolver.cpp54
-rw-r--r--src/lib/language/projectresolver.h4
-rw-r--r--src/lib/lib.qbs17
52 files changed, 2215 insertions, 786 deletions
diff --git a/share/qbs/modules/Qt/core/core.qbs b/share/qbs/modules/Qt/core/core.qbs
index c940a1bfe..8b5a6fdf1 100644
--- a/share/qbs/modules/Qt/core/core.qbs
+++ b/share/qbs/modules/Qt/core/core.qbs
@@ -202,6 +202,62 @@ Module {
fileTags: ["qhp"]
}
+// TagScanner {
+// name: "QtMocCppScanner"
+// inputTags: ["cpp"]
+// outputTags: ["includes_moc_hpp", "moc_cpp"]
+// scan: {
+// var fileTags = [];
+// var cppResult = scanCppForMocHints(artifact.filePath);
+// if (cppResult.hasQObject)
+// fileTags.push("moc_cpp");
+// if (!this.mocIncludes)
+// this.mocIncludes = {};
+// if (cppResult.mocIncludes) {
+// var c = cppResult.mocIncludes.length;
+// for (var i = 0; i < c; ++i) {
+// var headerBaseName = cppResult.mocIncludes[i];
+// headerBaseName = headerBaseName.slice(4).slice(0, -4); // remove moc_ and .cpp
+// this.mocIncludes[cppResult.mocIncludes[i]] = true;
+// }
+// }
+// return fileTags;
+// }
+// }
+
+// TagScanner {
+// name: "QtMocHppScanner"
+// dependencies: ["QtMocCppScanner"]
+// inputTags: ["hpp"]
+// outputTags: ["moc_hpp", "moc_hpp_inc"]
+// scan: {
+// var fileTags = [];
+// var hppFile = scanHppForMocHints(artifact.filePath);
+// if (hppFile.hasQObject) {
+// if (QtMocCppScanner.mocIncludes[artifact.completeBaseName])
+// fileTags.push("moc_hpp_inc");
+// else
+// fileTags.push("moc_hpp");
+// }
+// return fileTags;
+// }
+// }
+
+ TagScanner {
+ name: "QtMocCppScanner"
+ inputTags: ["cpp"]
+ outputTags: ["moc_cpp"]
+ scan: { /* implemented internally */ }
+ }
+
+ TagScanner {
+ name: "QtMocHppScanner"
+ dependencies: ["QtMocCppScanner"]
+ inputTags: ["hpp"]
+ outputTags: ["moc_hpp", "moc_hpp_inc"]
+ scan: { /* implemented internally */ }
+ }
+
Rule {
inputs: ["moc_cpp"]
diff --git a/src/lib/api/project.cpp b/src/lib/api/project.cpp
index 66346bde5..c38d72900 100644
--- a/src/lib/api/project.cpp
+++ b/src/lib/api/project.cpp
@@ -437,20 +437,20 @@ void ProjectPrivate::addFiles(const ProductData &product, const GroupData &group
addedSourceArtifacts << artifact;
groupContext.resolvedGroup->files << artifact;
}
- if (groupContext.resolvedProduct->enabled) {
- ArtifactsPerFileTagMap artifactsPerFileTag;
- foreach (const SourceArtifactConstPtr &sa, addedSourceArtifacts) {
- Artifact * const artifact = createArtifact(groupContext.resolvedProduct, sa, logger);
- foreach (const FileTag &ft, artifact->fileTags)
- artifactsPerFileTag[ft] += artifact;
- }
- RulesEvaluationContextPtr &evalContext
- = groupContext.resolvedProduct->topLevelProject()->buildData->evaluationContext;
- evalContext = QSharedPointer<RulesEvaluationContext>(new RulesEvaluationContext(logger));
- RulesApplicator(groupContext.resolvedProduct, artifactsPerFileTag, logger).applyAllRules();
- addTargetArtifacts(groupContext.resolvedProduct, artifactsPerFileTag, logger);
- evalContext.clear();
- }
+// if (groupContext.resolvedProduct->enabled) {
+// ArtifactsPerFileTagMap artifactsPerFileTag;
+// foreach (const SourceArtifactConstPtr &sa, addedSourceArtifacts) {
+// Artifact * const artifact = createArtifact(groupContext.resolvedProduct, sa, logger);
+// foreach (const FileTag &ft, artifact->fileTags)
+// artifactsPerFileTag[ft] += artifact;
+// }
+// RulesEvaluationContextPtr &evalContext
+// = groupContext.resolvedProduct->topLevelProject()->buildData->evaluationContext;
+// evalContext = QSharedPointer<RulesEvaluationContext>(new RulesEvaluationContext(logger));
+// RulesApplicator(groupContext.resolvedProduct, artifactsPerFileTag, logger).applyAllRules();
+// addTargetArtifacts(groupContext.resolvedProduct, artifactsPerFileTag, logger);
+// evalContext.clear();
+// }
doSanityChecks(internalProject, logger);
groupContext.currentGroup.d->filePaths << filesContext.absoluteFilePaths;
qSort(groupContext.currentGroup.d->filePaths);
@@ -864,7 +864,8 @@ QList<InstallableFile> Project::installableFilesForProduct(const ProductData &pr
}
if (internalProduct->enabled) {
QBS_CHECK(internalProduct->buildData);
- foreach (const Artifact * const artifact, internalProduct->buildData->artifacts) {
+ foreach (const Artifact * const artifact,
+ ArtifactSet::fromNodeSet(internalProduct->buildData->nodes)) {
if (artifact->artifactType == Artifact::SourceFile)
continue;
InstallableFile f;
diff --git a/src/lib/buildgraph/artifact.cpp b/src/lib/buildgraph/artifact.cpp
index 01bad8e36..da6a77ebc 100644
--- a/src/lib/buildgraph/artifact.cpp
+++ b/src/lib/buildgraph/artifact.cpp
@@ -30,7 +30,7 @@
#include "artifact.h"
#include "transformer.h"
-
+#include "buildgraphvisitor.h"
#include <language/propertymapinternal.h>
#include <tools/fileinfo.h>
#include <tools/persistence.h>
@@ -64,6 +64,17 @@ Artifact::~Artifact()
{
}
+void Artifact::accept(BuildGraphVisitor *visitor)
+{
+ if (visitor->visit(this))
+ acceptChildren(visitor);
+}
+
+QString Artifact::toString() const
+{
+ return QLatin1String("ARTIFACT ") + filePath();
+}
+
void Artifact::initialize()
{
artifactType = Unknown;
@@ -73,13 +84,24 @@ void Artifact::initialize()
alwaysUpdated = true;
}
+ArtifactSet Artifact::parentArtifacts() const
+{
+ return ArtifactSet::fromNodeSet(parents);
+}
+
+ArtifactSet Artifact::childArtifacts() const
+{
+ return ArtifactSet::fromNodeSet(children);
+}
+
void Artifact::load(PersistentPool &pool)
{
FileResourceBase::load(pool);
- pool.loadContainer(children);
+ BuildGraphNode::load(pool);
+ children.load(pool);
// restore parents of the loaded children
- for (ArtifactList::const_iterator it = children.constBegin(); it != children.constEnd(); ++it)
+ for (NodeSet::const_iterator it = children.constBegin(); it != children.constEnd(); ++it)
(*it)->parents.insert(this);
pool.loadContainer(childrenAddedByScanner);
@@ -90,7 +112,6 @@ void Artifact::load(PersistentPool &pool)
pool.stream()
>> fileTags
>> artifactType
- >> autoMocTimestamp
>> c;
alwaysUpdated = c;
}
@@ -98,8 +119,9 @@ void Artifact::load(PersistentPool &pool)
void Artifact::store(PersistentPool &pool) const
{
FileResourceBase::store(pool);
+ BuildGraphNode::store(pool);
// Do not store parents to avoid recursion.
- pool.storeContainer(children);
+ children.store(pool);
pool.storeContainer(childrenAddedByScanner);
pool.storeContainer(fileDependencies);
pool.store(properties);
@@ -107,7 +129,6 @@ void Artifact::store(PersistentPool &pool) const
pool.stream()
<< fileTags
<< artifactType
- << autoMocTimestamp
<< static_cast<unsigned char>(alwaysUpdated);
}
diff --git a/src/lib/buildgraph/artifact.h b/src/lib/buildgraph/artifact.h
index 55b5c81bc..c299bce90 100644
--- a/src/lib/buildgraph/artifact.h
+++ b/src/lib/buildgraph/artifact.h
@@ -30,14 +30,12 @@
#ifndef QBS_ARTIFACT_H
#define QBS_ARTIFACT_H
-#include "artifactlist.h"
+#include "artifactset.h"
#include "filedependency.h"
+#include "buildgraphnode.h"
#include "forward_decls.h"
#include <language/filetags.h>
-#include <language/forward_decls.h>
#include <tools/filetime.h>
-#include <tools/persistentobject.h>
-#include <tools/weakpointer.h>
#include <QSet>
#include <QString>
@@ -54,18 +52,18 @@ class Logger;
*
*
*/
-class Artifact : public FileResourceBase
+class Artifact : public FileResourceBase, public BuildGraphNode
{
public:
Artifact();
~Artifact();
- ArtifactList parents;
- ArtifactList children;
- ArtifactList childrenAddedByScanner;
+ void accept(BuildGraphVisitor *visitor);
+ QString toString() const;
+
+ ArtifactSet childrenAddedByScanner;
QSet<FileDependency *> fileDependencies;
FileTags fileTags;
- WeakPointer<ResolvedProduct> product;
TransformerPtr transformer;
PropertyMapPtr properties;
@@ -76,26 +74,19 @@ public:
Generated = 4
};
- enum BuildState
- {
- Untouched = 0,
- Buildable,
- Building,
- Built
- };
-
ArtifactType artifactType;
- FileTime autoMocTimestamp;
- BuildState buildState; // Do not serialize. Will be refreshed for every build.
bool inputsScanned : 1; // Do not serialize. Will be refreshed for every build.
bool timestampRetrieved : 1; // Do not serialize. Will be refreshed for every build.
bool alwaysUpdated : 1;
void initialize();
+ ArtifactSet parentArtifacts() const;
+ ArtifactSet childArtifacts() const;
private:
void load(PersistentPool &pool);
void store(PersistentPool &pool) const;
+ Type type() const { return ArtifactNodeType; }
};
// debugging helper
diff --git a/src/lib/buildgraph/artifactcleaner.cpp b/src/lib/buildgraph/artifactcleaner.cpp
index f4c5b47b0..96044a239 100644
--- a/src/lib/buildgraph/artifactcleaner.cpp
+++ b/src/lib/buildgraph/artifactcleaner.cpp
@@ -96,7 +96,7 @@ public:
void visitProduct(const ResolvedProductConstPtr &product)
{
m_product = product;
- ArtifactVisitor::visitProduct(product);
+ product->accept(this);
}
const QSet<QString> &directories() const { return m_directories; }
diff --git a/src/lib/buildgraph/artifactset.cpp b/src/lib/buildgraph/artifactset.cpp
new file mode 100644
index 000000000..c7174fec8
--- /dev/null
+++ b/src/lib/buildgraph/artifactset.cpp
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "artifactset.h"
+#include "artifact.h"
+
+namespace qbs {
+namespace Internal {
+
+ArtifactSet ArtifactSet::fromNodeSet(const NodeSet &nodes)
+{
+ ArtifactSet result;
+ result.reserve(nodes.count());
+ foreach (BuildGraphNode *node, nodes) {
+ Artifact *artifact = dynamic_cast<Artifact *>(node);
+ if (artifact)
+ result += artifact;
+ }
+ return result;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/buildgraph/artifactlist.cpp b/src/lib/buildgraph/artifactset.h
index 0e35ff190..ae513b739 100644
--- a/src/lib/buildgraph/artifactlist.cpp
+++ b/src/lib/buildgraph/artifactset.h
@@ -27,32 +27,24 @@
**
****************************************************************************/
-#include "artifactlist.h"
+#ifndef QBS_ARTIFACTSET_H
+#define QBS_ARTIFACTSET_H
+
+#include <QSet>
namespace qbs {
namespace Internal {
-ArtifactList::ArtifactList()
-{}
-
-ArtifactList::ArtifactList(const ArtifactList &other)
- : m_data(other.m_data)
-{}
+class Artifact;
+class NodeSet;
-ArtifactList &ArtifactList::unite(const ArtifactList &other)
+class ArtifactSet : public QSet<Artifact *>
{
- std::set<Artifact *>::const_iterator it = other.m_data.begin();
- for (; it != other.m_data.end(); ++it)
- m_data.insert(*it);
- return *this;
-}
-
-void ArtifactList::remove(Artifact *artifact)
-{
- iterator it = m_data.find(artifact);
- if (it != m_data.end())
- m_data.erase(it);
-}
+public:
+ static ArtifactSet fromNodeSet(const NodeSet &nodes);
+};
} // namespace Internal
} // namespace qbs
+
+#endif // QBS_ARTIFACTSET_H
diff --git a/src/lib/buildgraph/artifactvisitor.cpp b/src/lib/buildgraph/artifactvisitor.cpp
index 24829c51e..049b0c663 100644
--- a/src/lib/buildgraph/artifactvisitor.cpp
+++ b/src/lib/buildgraph/artifactvisitor.cpp
@@ -31,7 +31,6 @@
#include "artifact.h"
#include "productbuilddata.h"
#include <language/language.h>
-#include <tools/qbsassert.h>
namespace qbs {
namespace Internal {
@@ -40,34 +39,26 @@ ArtifactVisitor::ArtifactVisitor(int artifactType) : m_artifactType(artifactType
{
}
-void ArtifactVisitor::visitArtifact(Artifact *artifact)
+void ArtifactVisitor::visitProduct(const ResolvedProductConstPtr &product)
{
- QBS_ASSERT(artifact, return);
- if (m_allArtifacts.contains(artifact))
- return;
- m_allArtifacts << artifact;
- if (m_artifactType & artifact->artifactType)
- doVisit(artifact);
- else if (m_artifactType == Artifact::Generated)
- return;
- foreach (Artifact * const child, artifact->children)
- visitArtifact(child);
+ product->accept(this);
}
-void ArtifactVisitor::visitProduct(const ResolvedProductConstPtr &product)
+void ArtifactVisitor::visitProject(const ResolvedProjectConstPtr &project)
{
- if (!product->buildData)
- return;
- foreach (Artifact * const artifact, product->buildData->targetArtifacts)
- visitArtifact(artifact);
+ project->accept(this);
}
-void ArtifactVisitor::visitProject(const ResolvedProjectConstPtr &project)
+bool ArtifactVisitor::visit(Artifact *artifact)
{
- foreach (const ResolvedProductConstPtr &product, project->products)
- visitProduct(product);
- foreach (const ResolvedProjectConstPtr &subProject, project->subProjects)
- visitProject(subProject);
+ if (m_allArtifacts.contains(artifact))
+ return false;
+ m_allArtifacts << artifact;
+ if (m_artifactType & artifact->artifactType)
+ doVisit(artifact);
+ foreach (BuildGraphNode *child, artifact->children)
+ child->accept(this);
+ return false; // handle child traversal ourselves
}
} // namespace Internal
diff --git a/src/lib/buildgraph/artifactvisitor.h b/src/lib/buildgraph/artifactvisitor.h
index 6ff6a4f11..bf5861786 100644
--- a/src/lib/buildgraph/artifactvisitor.h
+++ b/src/lib/buildgraph/artifactvisitor.h
@@ -31,6 +31,7 @@
#include "forward_decls.h"
+#include "buildgraphvisitor.h"
#include <language/forward_decls.h>
#include <QList>
@@ -39,14 +40,14 @@
namespace qbs {
namespace Internal {
-class ArtifactVisitor
+class ArtifactVisitor : public BuildGraphVisitor
{
public:
ArtifactVisitor(int artifactType);
- virtual void visitArtifact(Artifact *artifact);
- virtual void visitProduct(const ResolvedProductConstPtr &product);
- virtual void visitProject(const ResolvedProjectConstPtr &project);
+ void visitProduct(const ResolvedProductConstPtr &product);
+ void visitProject(const ResolvedProjectConstPtr &project);
+ bool visit(Artifact *artifact);
private:
virtual void doVisit(Artifact *artifact) = 0;
diff --git a/src/lib/buildgraph/automoc.cpp b/src/lib/buildgraph/automoc.cpp
deleted file mode 100644
index 60a1ae318..000000000
--- a/src/lib/buildgraph/automoc.cpp
+++ /dev/null
@@ -1,344 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the Qt Build Suite.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-****************************************************************************/
-
-#include "automoc.h"
-#include "productbuilddata.h"
-#include "projectbuilddata.h"
-#include "buildgraph.h"
-#include "rulesapplicator.h"
-#include "scanresultcache.h"
-#include <buildgraph/artifact.h>
-#include <buildgraph/transformer.h>
-#include <language/language.h>
-#include <logging/translator.h>
-#include <tools/error.h>
-#include <tools/fileinfo.h>
-#include <tools/scannerpluginmanager.h>
-
-namespace qbs {
-namespace Internal {
-
-AutoMoc::AutoMoc(const Logger &logger, QObject *parent)
- : QObject(parent)
- , m_scanResultCache(0)
- , m_logger(logger)
-{
-}
-
-void AutoMoc::setScanResultCache(ScanResultCache *scanResultCache)
-{
- m_scanResultCache = scanResultCache;
-}
-
-void AutoMoc::apply(const ResolvedProductPtr &product)
-{
- if (cppScanners().isEmpty() || hppScanners().isEmpty())
- throw ErrorInfo("C++ scanner cannot be loaded.");
-
- Artifact *pluginMetaDataFile = 0;
- Artifact *pchFile = 0;
- QList<QPair<Artifact *, FileType> > artifactsToMoc;
- QSet<QString> includedMocCppFiles;
- const FileTime currentTime = FileTime::currentTime();
- ArtifactList::const_iterator it = product->buildData->artifacts.begin();
- for (; it != product->buildData->artifacts.end(); ++it) {
- Artifact *artifact = *it;
- if (!pchFile || !pluginMetaDataFile) {
- foreach (const FileTag &fileTag, artifact->fileTags) {
- if (fileTag == "cpp_pch")
- pchFile = artifact;
- else if (fileTag == "qt_plugin_metadata")
- pluginMetaDataFile = artifact;
- }
- }
-
- if (!pluginMetaDataFile && artifact->fileTags.contains("qt_plugin_metadata")) {
- if (m_logger.debugEnabled()) {
- m_logger.qbsDebug() << "[AUTOMOC] found Qt plugin metadata file "
- << artifact->filePath();
- }
- pluginMetaDataFile = artifact;
- }
- if (artifact->artifactType != Artifact::SourceFile)
- continue;
- if (artifact->timestamp() < artifact->autoMocTimestamp)
- continue;
- artifact->autoMocTimestamp = currentTime;
- const FileType fileType = AutoMoc::fileType(artifact);
- if (fileType == UnknownFileType)
- continue;
- FileTag mocFileTag;
- bool alreadyMocced = isVictimOfMoc(artifact, fileType, mocFileTag);
- bool hasQObjectMacro;
- scan(artifact, fileType, hasQObjectMacro, includedMocCppFiles);
- if (hasQObjectMacro && !alreadyMocced) {
- artifactsToMoc += qMakePair(artifact, fileType);
- } else if (!hasQObjectMacro && alreadyMocced) {
- unmoc(artifact, mocFileTag);
- }
- }
-
- Artifact *pluginHeaderFile = 0;
- ArtifactsPerFileTagMap artifactsPerFileTag;
- for (int i = artifactsToMoc.count(); --i >= 0;) {
- const QPair<Artifact *, FileType> &p = artifactsToMoc.at(i);
- Artifact * const artifact = p.first;
- FileType fileType = p.second;
- foreach (const FileTag &fileTag, artifact->fileTags) {
- if (fileTag == "moc_hpp") {
- const QString mocFileName = generateMocFileName(artifact, fileType);
- if (includedMocCppFiles.contains(mocFileName)) {
- FileTag newFileTag = "moc_hpp_inc";
- artifact->fileTags -= fileTag;
- artifact->fileTags += newFileTag;
- artifactsPerFileTag[newFileTag].insert(artifact);
- continue;
- }
- } else if (fileTag == "moc_plugin_hpp") {
- if (m_logger.debugEnabled()) {
- m_logger.qbsDebug() << "[AUTOMOC] found Qt plugin header file "
- << artifact->filePath();
- }
- FileTag newFileTag = "moc_hpp";
- artifact->fileTags -= fileTag;
- artifact->fileTags += newFileTag;
- artifactsPerFileTag[newFileTag].insert(artifact);
- pluginHeaderFile = artifact;
- }
- artifactsPerFileTag[fileTag].insert(artifact);
- }
- }
-
- if (pchFile)
- artifactsPerFileTag["cpp_pch"] += pchFile;
- if (!artifactsPerFileTag.isEmpty()) {
- emit reportCommandDescription(QLatin1String("automoc"),
- Tr::tr("Applying moc rules for '%1'.")
- .arg(product->name));
- RulesApplicator(product, artifactsPerFileTag, m_logger).applyAllRules();
- }
- if (pluginHeaderFile && pluginMetaDataFile) {
- // Make every artifact that is dependent of the header file also
- // dependent of the plugin metadata file.
- foreach (Artifact *outputOfHeader, pluginHeaderFile->parents)
- loggedConnect(outputOfHeader, pluginMetaDataFile, m_logger);
- }
-
- product->topLevelProject()->buildData->updateNodesThatMustGetNewTransformer(m_logger);
-}
-
-QString AutoMoc::generateMocFileName(Artifact *artifact, FileType fileType)
-{
- QString mocFileName;
- switch (fileType) {
- case UnknownFileType:
- break;
- case HppFileType:
- mocFileName = "moc_" + FileInfo::baseName(artifact->filePath()) + ".cpp";
- break;
- case CppFileType:
- mocFileName = FileInfo::baseName(artifact->filePath()) + ".moc";
- break;
- }
- return mocFileName;
-}
-
-AutoMoc::FileType AutoMoc::fileType(Artifact *artifact)
-{
- foreach (const FileTag &fileTag, artifact->fileTags)
- if (fileTag == "hpp")
- return HppFileType;
- else if (fileTag == "cpp")
- return CppFileType;
- return UnknownFileType;
-}
-
-void AutoMoc::scan(Artifact *artifact, FileType fileType, bool &hasQObjectMacro,
- QSet<QString> &includedMocCppFiles)
-{
- if (m_logger.traceEnabled())
- m_logger.qbsTrace() << "[AUTOMOC] checks " << relativeArtifactFileName(artifact);
-
- hasQObjectMacro = false;
-
- foreach (ScannerPlugin *scanner, fileType == HppFileType ? hppScanners() : cppScanners()) {
- ScanResultCache::Result scanResult = m_scanResultCache->value(artifact->filePath());
- if (!scanResult.valid) {
- scanResult.valid = true;
- void *opaq = scanner->open(artifact->filePath().utf16(),
- ScanForDependenciesFlag | ScanForFileTagsFlag);
- if (!opaq || !scanner->additionalFileTags)
- continue;
-
- int length = 0;
- const char **szFileTagsFromScanner = scanner->additionalFileTags(opaq, &length);
- if (szFileTagsFromScanner && length > 0) {
- for (int i = length; --i >= 0;)
- scanResult.additionalFileTags += szFileTagsFromScanner[i];
- }
-
- forever {
- int flags = 0;
- const char *szOutFilePath = scanner->next(opaq, &length, &flags);
- if (szOutFilePath == 0)
- break;
- QString includedFilePath = QString::fromLocal8Bit(szOutFilePath, length);
- if (includedFilePath.isEmpty())
- continue;
- bool isLocalInclude = (flags & SC_LOCAL_INCLUDE_FLAG);
- scanResult.deps += ScanResultCache::Dependency(includedFilePath, isLocalInclude);
- }
-
- scanner->close(opaq);
- m_scanResultCache->insert(artifact->filePath(), scanResult);
- }
-
- foreach (const FileTag &tag, scanResult.additionalFileTags) {
- artifact->fileTags.insert(tag);
- if (tag.name().startsWith("moc")) {
- hasQObjectMacro = true;
- if (m_logger.traceEnabled())
- m_logger.qbsTrace() << "[AUTOMOC] finds Q_OBJECT macro";
- }
- }
-
- foreach (const ScanResultCache::Dependency &dependency, scanResult.deps) {
- const QString &includedFilePath = dependency.filePath();
- if (includedFilePath.startsWith("moc_") && includedFilePath.endsWith(".cpp")) {
- if (m_logger.traceEnabled())
- m_logger.qbsTrace() << "[AUTOMOC] finds included file: " << includedFilePath;
- includedMocCppFiles += includedFilePath;
- }
- }
- }
-}
-
-static FileTags provideMocHeaderFileTags()
-{
- FileTags fileTags;
- fileTags << "moc_hpp" << "moc_hpp_inc" << "moc_plugin_hpp";
- return fileTags;
-}
-
-bool AutoMoc::isVictimOfMoc(Artifact *artifact, FileType fileType, FileTag &foundMocFileTag)
-{
- static const FileTags mocHeaderFileTags = provideMocHeaderFileTags();
- static const FileTag mocCppFileTag = "moc_cpp";
- foundMocFileTag.clear();
- switch (fileType) {
- case UnknownFileType:
- break;
- case HppFileType:
- foreach (const FileTag &fileTag, artifact->fileTags) {
- if (mocHeaderFileTags.contains(fileTag)) {
- foundMocFileTag = fileTag;
- break;
- }
- }
- break;
- case CppFileType:
- if (artifact->fileTags.contains(mocCppFileTag))
- foundMocFileTag = mocCppFileTag;
- break;
- }
- return foundMocFileTag.isValid();
-}
-
-void AutoMoc::unmoc(Artifact *artifact, const FileTag &mocFileTag)
-{
- if (m_logger.traceEnabled())
- m_logger.qbsTrace() << "[AUTOMOC] unmoc'ing " << relativeArtifactFileName(artifact);
-
- artifact->fileTags.remove(mocFileTag);
-
- Artifact *generatedMocArtifact = 0;
- foreach (Artifact *parent, artifact->parents) {
- foreach (const FileTag &fileTag, parent->fileTags) {
- if (fileTag == "hpp" || fileTag == "cpp") {
- generatedMocArtifact = parent;
- break;
- }
- }
- }
-
- if (!generatedMocArtifact) {
- m_logger.qbsTrace() << "[AUTOMOC] generated moc artifact could not be found";
- return;
- }
-
- TopLevelProject * const project = artifact->product->topLevelProject();
- if (mocFileTag == "moc_hpp") {
- Artifact *mocObjArtifact = 0;
- foreach (Artifact *parent, generatedMocArtifact->parents) {
- foreach (const FileTag &fileTag, parent->fileTags) {
- if (fileTag == "obj" || fileTag == "fpicobj") {
- mocObjArtifact = parent;
- break;
- }
- }
- }
-
- if (!mocObjArtifact) {
- m_logger.qbsTrace() << "[AUTOMOC] generated moc obj artifact could not be found";
- } else {
- if (m_logger.traceEnabled()) {
- m_logger.qbsTrace() << "[AUTOMOC] removing moc obj artifact "
- << relativeArtifactFileName(mocObjArtifact);
- }
- project->buildData->removeArtifact(mocObjArtifact, m_logger);
- delete mocObjArtifact;
- }
- }
-
- if (m_logger.traceEnabled()) {
- m_logger.qbsTrace() << "[AUTOMOC] removing generated artifact "
- << relativeArtifactFileName(generatedMocArtifact);
- }
- project->buildData->removeArtifact(generatedMocArtifact, m_logger);
- delete generatedMocArtifact;
-}
-
-const QList<ScannerPlugin *> &AutoMoc::cppScanners() const
-{
- if (m_cppScanners.isEmpty())
- m_cppScanners = ScannerPluginManager::scannersForFileTag("cpp");
-
- return m_cppScanners;
-}
-
-const QList<ScannerPlugin *> &AutoMoc::hppScanners() const
-{
- if (m_hppScanners.isEmpty())
- m_hppScanners = ScannerPluginManager::scannersForFileTag("hpp");
-
- return m_hppScanners;
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/buildgraph/buildgraph.cpp b/src/lib/buildgraph/buildgraph.cpp
index f8c4f8b6a..81986be49 100644
--- a/src/lib/buildgraph/buildgraph.cpp
+++ b/src/lib/buildgraph/buildgraph.cpp
@@ -209,14 +209,14 @@ void setupScriptEngineForProduct(ScriptEngine *engine, const ResolvedProductCons
rule->module->name.isEmpty() ? QScriptValue() : rule->module->name);
}
-bool findPath(Artifact *u, Artifact *v, QList<Artifact*> &path)
+bool findPath(BuildGraphNode *u, BuildGraphNode *v, QList<BuildGraphNode *> &path)
{
if (u == v) {
path.append(v);
return true;
}
- for (ArtifactList::const_iterator it = u->children.begin(); it != u->children.end(); ++it) {
+ for (NodeSet::const_iterator it = u->children.begin(); it != u->children.end(); ++it) {
if (findPath(*it, v, path)) {
path.prepend(u);
return true;
@@ -235,7 +235,7 @@ bool findPath(Artifact *u, Artifact *v, QList<Artifact*> &path)
* also: children means i depend on or i am produced by
* parent means "produced by me" or "depends on me"
*/
-void connect(Artifact *p, Artifact *c)
+void connect(BuildGraphNode *p, BuildGraphNode *c)
{
QBS_CHECK(p != c);
p->children.insert(c);
@@ -243,28 +243,36 @@ void connect(Artifact *p, Artifact *c)
p->product->topLevelProject()->buildData->isDirty = true;
}
-void loggedConnect(Artifact *u, Artifact *v, const Logger &logger)
+void loggedConnect(BuildGraphNode *u, BuildGraphNode *v, const Logger &logger)
{
QBS_CHECK(u != v);
if (logger.traceEnabled()) {
logger.qbsTrace() << QString::fromLocal8Bit("[BG] connect '%1' -> '%2'")
- .arg(relativeArtifactFileName(u), relativeArtifactFileName(v));
+ .arg(u->toString(), v->toString());
}
connect(u, v);
}
-static bool existsPath(Artifact *u, Artifact *v)
+static bool existsPath(BuildGraphNode *u, BuildGraphNode *v)
{
if (u == v)
return true;
- for (ArtifactList::const_iterator it = u->children.begin(); it != u->children.end(); ++it)
+ for (NodeSet::const_iterator it = u->children.begin(); it != u->children.end(); ++it)
if (existsPath(*it, v))
return true;
return false;
}
+static QStringList toStringList(const QList<BuildGraphNode *> &path)
+{
+ QStringList lst;
+ foreach (BuildGraphNode *node, path)
+ lst << node->toString();
+ return lst;
+}
+
bool safeConnect(Artifact *u, Artifact *v, const Logger &logger)
{
QBS_CHECK(u != v);
@@ -274,7 +282,7 @@ bool safeConnect(Artifact *u, Artifact *v, const Logger &logger)
}
if (existsPath(v, u)) {
- QList<Artifact *> circle;
+ QList<BuildGraphNode *> circle;
findPath(v, u, circle);
logger.qbsTrace() << "[BG] safeConnect: circle detected " << toStringList(circle);
return false;
@@ -284,17 +292,22 @@ bool safeConnect(Artifact *u, Artifact *v, const Logger &logger)
return true;
}
-void disconnect(Artifact *u, Artifact *v, const Logger &logger)
+void disconnect(BuildGraphNode *u, BuildGraphNode *v, const Logger &logger)
{
if (logger.traceEnabled()) {
logger.qbsTrace() << QString::fromLocal8Bit("[BG] disconnect: '%1' '%2'")
- .arg(relativeArtifactFileName(u), relativeArtifactFileName(v));
+ .arg(u->toString(), v->toString());
}
u->children.remove(v);
- u->childrenAddedByScanner.remove(v);
v->parents.remove(u);
}
+void disconnect(Artifact *u, Artifact *v, const Logger &logger)
+{
+ disconnect(static_cast<BuildGraphNode *>(u), static_cast<BuildGraphNode *>(v), logger);
+ u->childrenAddedByScanner.remove(v);
+}
+
void removeGeneratedArtifactFromDisk(Artifact *artifact, const Logger &logger)
{
if (artifact->artifactType != Artifact::Generated)
@@ -384,7 +397,7 @@ void insertArtifact(const ResolvedProductPtr &product, Artifact *artifact, const
{
QBS_CHECK(!artifact->product);
QBS_CHECK(!artifact->filePath().isEmpty());
- QBS_CHECK(!product->buildData->artifacts.contains(artifact));
+ QBS_CHECK(!product->buildData->nodes.contains(artifact));
#ifdef QT_DEBUG
foreach (const ResolvedProductConstPtr &otherProduct, product->project->products) {
if (lookupArtifact(otherProduct, artifact->filePath())) {
@@ -401,7 +414,7 @@ void insertArtifact(const ResolvedProductPtr &product, Artifact *artifact, const
}
}
#endif
- product->buildData->artifacts.insert(artifact);
+ product->buildData->nodes.insert(artifact);
artifact->product = product;
product->topLevelProject()->buildData->insertIntoLookupTable(artifact);
product->topLevelProject()->buildData->isDirty = true;
@@ -414,6 +427,9 @@ void insertArtifact(const ResolvedProductPtr &product, Artifact *artifact, const
static void doSanityChecksForProduct(const ResolvedProductConstPtr &product, const Logger &logger)
{
+ Q_UNUSED(product);
+ Q_UNUSED(logger);
+#if 0
logger.qbsDebug() << "Sanity checking product '" << product->name << "'";
CycleDetector cycleDetector(logger);
cycleDetector.visitProduct(product);
@@ -443,7 +459,7 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product, con
QBS_CHECK(transformer);
QBS_CHECK(transformer->outputs.contains(artifact));
- ArtifactList transformerOutputChildren;
+ ArtifactSet transformerOutputChildren;
foreach (const Artifact * const output, transformer->outputs) {
QBS_CHECK(output->transformer == transformer);
transformerOutputChildren.unite(output->children);
@@ -460,6 +476,7 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product, con
foreach (Artifact * const transformerInput, transformer->inputs)
QBS_CHECK(transformerOutputChildren.contains(transformerInput));
}
+#endif
}
static void doSanityChecks(const ResolvedProjectPtr &project, QSet<QString> &productNames,
diff --git a/src/lib/buildgraph/buildgraph.h b/src/lib/buildgraph/buildgraph.h
index 9f46b1ecb..63296735c 100644
--- a/src/lib/buildgraph/buildgraph.h
+++ b/src/lib/buildgraph/buildgraph.h
@@ -30,8 +30,6 @@
#define QBS_BUILDGRAPH_H
#include "forward_decls.h"
-#include "rulesapplicator.h"
-
#include <language/forward_decls.h>
#include <QScriptValue>
@@ -39,6 +37,7 @@
namespace qbs {
namespace Internal {
+class BuildGraphNode;
class Logger;
class ScriptEngine;
class ScriptPropertyObserver;
@@ -60,16 +59,15 @@ Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const Artifact
Artifact *createArtifact(const ResolvedProductPtr &product,
const SourceArtifactConstPtr &sourceArtifact, const Logger &logger);
void insertArtifact(const ResolvedProductPtr &product, Artifact *artifact, const Logger &logger);
-void addTargetArtifacts(const ResolvedProductPtr &product,
- ArtifactsPerFileTagMap &artifactsPerFileTag, const Logger &logger);
void dumpProductBuildData(const ResolvedProductConstPtr &product);
-bool findPath(Artifact *u, Artifact *v, QList<Artifact*> &path);
-void connect(Artifact *p, Artifact *c);
-void loggedConnect(Artifact *u, Artifact *v, const Logger &logger);
+bool findPath(BuildGraphNode *u, BuildGraphNode *v, QList<BuildGraphNode*> &path);
+void connect(BuildGraphNode *p, BuildGraphNode *c);
+void loggedConnect(BuildGraphNode *u, BuildGraphNode *v, const Logger &logger);
bool safeConnect(Artifact *u, Artifact *v, const Logger &logger);
void removeGeneratedArtifactFromDisk(Artifact *artifact, const Logger &logger);
+void disconnect(BuildGraphNode *u, BuildGraphNode *v, const Logger &logger);
void disconnect(Artifact *u, Artifact *v, const Logger &logger);
void setupScriptEngineForFile(ScriptEngine *engine, const ResolvedFileContextConstPtr &fileContext,
@@ -81,15 +79,6 @@ QString relativeArtifactFileName(const Artifact *artifact); // Debugging helpers
void doSanityChecks(const ResolvedProjectPtr &project, const Logger &logger);
-template <typename T>
-QStringList toStringList(const T &artifactContainer)
-{
- QStringList l;
- foreach (Artifact *n, artifactContainer)
- l.append(relativeArtifactFileName(n));
- return l;
-}
-
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/buildgraph/buildgraph.pri b/src/lib/buildgraph/buildgraph.pri
index fa091f1b3..d94ef4528 100644
--- a/src/lib/buildgraph/buildgraph.pri
+++ b/src/lib/buildgraph/buildgraph.pri
@@ -2,11 +2,11 @@ SOURCES += \
$$PWD/abstractcommandexecutor.cpp \
$$PWD/artifact.cpp \
$$PWD/artifactcleaner.cpp \
- $$PWD/artifactlist.cpp \
+ $$PWD/artifactset.cpp \
$$PWD/artifactvisitor.cpp \
- $$PWD/automoc.cpp \
$$PWD/buildgraph.cpp \
$$PWD/buildgraphloader.cpp \
+ $$PWD/buildgraphnode.cpp \
$$PWD/command.cpp \
$$PWD/cycledetector.cpp \
$$PWD/executor.cpp \
@@ -14,26 +14,32 @@ SOURCES += \
$$PWD/filedependency.cpp \
$$PWD/inputartifactscanner.cpp \
$$PWD/jscommandexecutor.cpp \
+ $$PWD/moctagscanner.cpp \
+ $$PWD/nodeset.cpp \
$$PWD/processcommandexecutor.cpp \
$$PWD/productbuilddata.cpp \
$$PWD/productinstaller.cpp \
$$PWD/projectbuilddata.cpp \
$$PWD/rulegraph.cpp \
+ $$PWD/rulenode.cpp \
$$PWD/rulesapplicator.cpp \
$$PWD/rulesevaluationcontext.cpp \
$$PWD/scanresultcache.cpp \
+ $$PWD/tagscannerrunner.cpp \
$$PWD/timestampsupdater.cpp \
- $$PWD/transformer.cpp
+ $$PWD/transformer.cpp \
+ buildgraph/phonytarget.cpp
HEADERS += \
$$PWD/abstractcommandexecutor.h \
$$PWD/artifact.h \
$$PWD/artifactcleaner.h \
- $$PWD/artifactlist.h \
+ $$PWD/artifactset.h \
$$PWD/artifactvisitor.h \
- $$PWD/automoc.h \
$$PWD/buildgraph.h \
$$PWD/buildgraphloader.h \
+ $$PWD/buildgraphnode.h \
+ $$PWD/buildgraphvisitor.h \
$$PWD/command.h \
$$PWD/cycledetector.h \
$$PWD/executor.h \
@@ -42,16 +48,21 @@ HEADERS += \
$$PWD/forward_decls.h \
$$PWD/inputartifactscanner.h \
$$PWD/jscommandexecutor.h \
+ $$PWD/moctagscanner.h \
+ $$PWD/nodeset.h \
$$PWD/processcommandexecutor.h \
$$PWD/productbuilddata.h \
$$PWD/productinstaller.h \
$$PWD/projectbuilddata.h \
$$PWD/rulegraph.h \
+ $$PWD/rulenode.h \
$$PWD/rulesapplicator.h \
$$PWD/rulesevaluationcontext.h \
$$PWD/scanresultcache.h \
+ $$PWD/tagscannerrunner.h \
$$PWD/timestampsupdater.h \
- $$PWD/transformer.h
+ $$PWD/transformer.h \
+ buildgraph/phonytarget.h
all_tests {
HEADERS += $$PWD/tst_buildgraph.h
diff --git a/src/lib/buildgraph/buildgraphloader.cpp b/src/lib/buildgraph/buildgraphloader.cpp
index 8c3639104..2c8764742 100644
--- a/src/lib/buildgraph/buildgraphloader.cpp
+++ b/src/lib/buildgraph/buildgraphloader.cpp
@@ -29,7 +29,7 @@
#include "buildgraphloader.h"
#include "artifact.h"
-#include "artifactlist.h"
+#include "artifactset.h"
#include "buildgraph.h"
#include "command.h"
#include "cycledetector.h"
@@ -85,8 +85,10 @@ static void restoreBackPointers(const ResolvedProjectPtr &project)
product->project = project;
if (!product->buildData)
continue;
- foreach (Artifact * const a, product->buildData->artifacts)
- project->topLevelProject()->buildData->insertIntoLookupTable(a);
+ foreach (BuildGraphNode * const n, product->buildData->nodes) {
+ if (Artifact *a = dynamic_cast<Artifact *>(n))
+ project->topLevelProject()->buildData->insertIntoLookupTable(a);
+ }
}
foreach (const ResolvedProjectPtr &subProject, project->subProjects) {
@@ -216,8 +218,8 @@ void BuildGraphLoader::trackProjectChanges(const SetupProjectParameters &paramet
// If the product gets temporarily removed, its artifacts will get disconnected
// and this structural information will no longer be directly available from them.
- foreach (const Artifact * const a, product->buildData->artifacts)
- childLists.insert(a, a->children);
+ foreach (const Artifact * const a, ArtifactSet::fromNodeSet(product->buildData->nodes))
+ childLists.insert(a, ArtifactSet::fromNodeSet(a->children));
}
}
@@ -247,7 +249,7 @@ void BuildGraphLoader::trackProjectChanges(const SetupProjectParameters &paramet
} else {
if (restoredProduct->enabled) {
QBS_CHECK(restoredProduct->buildData);
- foreach (Artifact * const a, newlyResolvedProduct->buildData->artifacts) {
+ foreach (Artifact * const a, ArtifactSet::fromNodeSet(newlyResolvedProduct->buildData->nodes)) {
const bool removeFromDisk = a->artifactType == Artifact::Generated;
newlyResolvedProduct->topLevelProject()->buildData->removeArtifact(a,
m_logger, removeFromDisk, true);
@@ -257,8 +259,8 @@ void BuildGraphLoader::trackProjectChanges(const SetupProjectParameters &paramet
productsWithChangedFiles.removeOne(restoredProduct);
}
if (newlyResolvedProduct->buildData) {
- foreach (Artifact * const a, newlyResolvedProduct->buildData->artifacts)
- a->product = newlyResolvedProduct;
+ foreach (BuildGraphNode *node, newlyResolvedProduct->buildData->nodes)
+ node->product = newlyResolvedProduct;
}
// Keep in list if build data still needs to be resolved.
@@ -391,7 +393,7 @@ void BuildGraphLoader::checkAllProductsForChanges(const QList<ResolvedProductPtr
if (!newlyResolvedProduct)
continue;
if (!productsWithChangedFiles.contains(restoredProduct)
- && !sourceArtifactListsAreEqual(restoredProduct->allFiles(),
+ && !sourceArtifactSetsAreEqual(restoredProduct->allFiles(),
newlyResolvedProduct->allFiles())) {
m_logger.qbsDebug() << "File list of product '" << restoredProduct->name
<< "' was changed.";
@@ -462,7 +464,7 @@ bool BuildGraphLoader::checkForPropertyChanges(const ResolvedProductPtr &restore
return true;
}
QSet<TransformerConstPtr> seenTransformers;
- foreach (Artifact * const artifact, restoredProduct->buildData->artifacts) {
+ foreach (Artifact *artifact, ArtifactSet::fromNodeSet(restoredProduct->buildData->nodes)) {
const TransformerConstPtr transformer = artifact->transformer;
if (!transformer || seenTransformers.contains(transformer))
continue;
@@ -483,7 +485,7 @@ void BuildGraphLoader::onProductRemoved(const ResolvedProductPtr &product,
product->project->products.removeOne(product);
if (product->buildData) {
- foreach (Artifact *artifact, product->buildData->artifacts)
+ foreach (Artifact *artifact, ArtifactSet::fromNodeSet(product->buildData->nodes))
projectBuildData->removeArtifact(artifact, m_logger, removeArtifactsFromDisk, false);
}
}
@@ -495,9 +497,8 @@ void BuildGraphLoader::onProductFileListChanged(const ResolvedProductPtr &restor
QBS_CHECK(newlyResolvedProduct->enabled);
- ArtifactsPerFileTagMap artifactsPerFileTag;
QList<Artifact *> addedArtifacts;
- ArtifactList artifactsToRemove;
+ ArtifactSet artifactsToRemove;
QHash<QString, SourceArtifactConstPtr> oldArtifacts, newArtifacts;
const QList<SourceArtifactPtr> restoredProductAllFiles = restoredProduct->allEnabledFiles();
@@ -567,37 +568,29 @@ void BuildGraphLoader::onProductFileListChanged(const ResolvedProductPtr &restor
// handle added filetags
foreach (const FileTag &addedFileTag, changedArtifact->fileTags - a->fileTags) {
artifact->fileTags += addedFileTag;
- artifactsPerFileTag[addedFileTag] += artifact;
+ restoredProduct->buildData->registerAddedFileTag(addedFileTag, artifact);
}
// handle removed filetags
foreach (const FileTag &removedFileTag, a->fileTags - changedArtifact->fileTags) {
artifact->fileTags -= removedFileTag;
- foreach (Artifact *parent, artifact->parents) {
- if (parent->transformer && parent->transformer->rule->inputs.contains(removedFileTag)) {
- // this parent has been created because of the removed filetag
- newlyResolvedProduct->topLevelProject()->buildData
- ->removeArtifactAndExclusiveDependents(parent, m_logger, true,
- &artifactsToRemove);
- }
- }
+ restoredProduct->buildData->registerRemovedFileTag(removedFileTag, artifact);
}
}
}
- // apply rules for new artifacts
- foreach (Artifact *artifact, addedArtifacts)
- foreach (const FileTag &ft, artifact->fileTags)
- artifactsPerFileTag[ft] += artifact;
+#if 0
RulesApplicator(newlyResolvedProduct, artifactsPerFileTag, m_logger).applyAllRules();
-
addTargetArtifacts(newlyResolvedProduct, artifactsPerFileTag, m_logger);
-
// parents of removed artifacts must update their transformers
- foreach (Artifact *removedArtifact, artifactsToRemove)
- foreach (Artifact *parent, removedArtifact->parents)
+ foreach (Artifact *removedArtifact, artifactsToRemove) {
+ foreach (BuildGraphNode *parentNode, removedArtifact->parents) {
newlyResolvedProduct->topLevelProject()->buildData->artifactsThatMustGetNewTransformers += parent;
newlyResolvedProduct->topLevelProject()->buildData->updateNodesThatMustGetNewTransformer(m_logger);
+ continue;
+ }
+ }
+#endif
// delete all removed artifacts physically from the disk
foreach (Artifact *artifact, artifactsToRemove) {
@@ -682,7 +675,8 @@ void BuildGraphLoader::replaceFileDependencyWithArtifact(const ResolvedProductPt
foreach (const ResolvedProductPtr &product, fileDepProduct->topLevelProject()->allProducts()) {
if (!product->buildData)
continue;
- foreach (Artifact *artifactInProduct, product->buildData->artifacts) {
+ foreach (BuildGraphNode *nodeInProduct, product->buildData->nodes) {
+ Artifact *artifactInProduct = dynamic_cast<Artifact *>(nodeInProduct);
if (artifactInProduct->fileDependencies.contains(filedep)) {
artifactInProduct->fileDependencies.remove(filedep);
loggedConnect(artifactInProduct, artifact, m_logger);
@@ -721,7 +715,8 @@ void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restore
"product '%1'").arg(restoredProduct->name);
}
- foreach (Artifact *artifact, newlyResolvedProduct->buildData->artifacts) {
+ foreach (Artifact *artifact,
+ ArtifactSet::fromNodeSet(newlyResolvedProduct->buildData->nodes)) {
if (m_logger.traceEnabled()) {
m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] artifact '%1'")
.arg(artifact->fileName());
@@ -755,20 +750,5 @@ void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restore
}
}
-void addTargetArtifacts(const ResolvedProductPtr &product,
- ArtifactsPerFileTagMap &artifactsPerFileTag, const Logger &logger)
-{
- foreach (const FileTag &fileTag, product->fileTags) {
- foreach (Artifact * const artifact, artifactsPerFileTag.value(fileTag)) {
- if (artifact->artifactType == Artifact::Generated)
- product->buildData->targetArtifacts += artifact;
- }
- }
- if (product->buildData->targetArtifacts.isEmpty()) {
- const QString msg = QString::fromLocal8Bit("No artifacts generated for product '%1'.");
- logger.qbsDebug() << msg.arg(product->name);
- }
-}
-
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/buildgraph/buildgraphloader.h b/src/lib/buildgraph/buildgraphloader.h
index fed032f72..2c072b10c 100644
--- a/src/lib/buildgraph/buildgraphloader.h
+++ b/src/lib/buildgraph/buildgraphloader.h
@@ -31,7 +31,7 @@
#include "forward_decls.h"
-#include <buildgraph/artifactlist.h>
+#include <buildgraph/artifactset.h>
#include <language/forward_decls.h>
#include <logging/logger.h>
@@ -45,6 +45,7 @@ namespace Internal {
class FileDependency;
class FileResourceBase;
class FileTime;
+class NodeSet;
class Property;
class BuildGraphLoadResult
@@ -92,7 +93,7 @@ private:
void onProductFileListChanged(const ResolvedProductPtr &restoredProduct,
const ResolvedProductPtr &newlyResolvedProduct, const ProjectBuildData *oldBuildData);
void removeArtifactAndExclusiveDependents(Artifact *artifact,
- ArtifactList *removedArtifacts = 0);
+ ArtifactSet *removedArtifacts = 0);
bool checkForPropertyChanges(const TransformerConstPtr &restoredTrafo,
const ResolvedProductPtr &freshProduct);
bool checkForPropertyChange(const Property &restoredProperty,
@@ -100,7 +101,7 @@ private:
void replaceFileDependencyWithArtifact(const ResolvedProductPtr &fileDepProduct,
FileDependency *filedep, Artifact *artifact);
- typedef QHash<const Artifact *, ArtifactList> ChildListHash;
+ typedef QHash<const Artifact *, ArtifactSet> ChildListHash;
void rescueOldBuildData(const ResolvedProductConstPtr &restoredProduct,
const ResolvedProductPtr &newlyResolvedProduct,
const ProjectBuildData *oldBuildData,
diff --git a/src/lib/buildgraph/buildgraphnode.cpp b/src/lib/buildgraph/buildgraphnode.cpp
new file mode 100644
index 000000000..733d7e6c8
--- /dev/null
+++ b/src/lib/buildgraph/buildgraphnode.cpp
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "buildgraphnode.h"
+
+#include "buildgraphvisitor.h"
+#include "projectbuilddata.h"
+#include <language/language.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/qbsassert.h>
+
+//#include <qglobal.h>
+
+namespace qbs {
+namespace Internal {
+
+BuildGraphNode::BuildGraphNode()
+{
+}
+
+BuildGraphNode::~BuildGraphNode()
+{
+}
+
+void BuildGraphNode::connectChild(BuildGraphNode *child)
+{
+ QBS_CHECK(this != child);
+ children.insert(child);
+ child->parents.insert(this);
+ product->topLevelProject()->buildData->isDirty = true;
+}
+
+void BuildGraphNode::acceptChildren(BuildGraphVisitor *visitor)
+{
+ foreach (BuildGraphNode *child, children)
+ child->accept(visitor);
+}
+
+void BuildGraphNode::load(PersistentPool &pool)
+{
+ children.load(pool);
+ // Parents must be updated after loading all nodes.
+}
+
+void BuildGraphNode::store(PersistentPool &pool) const
+{
+ children.store(pool);
+ // Do not store parents to avoid recursion.
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/buildgraph/buildgraphnode.h b/src/lib/buildgraph/buildgraphnode.h
new file mode 100644
index 000000000..2ec0fce97
--- /dev/null
+++ b/src/lib/buildgraph/buildgraphnode.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_BUILDGRAPHNODE_H
+#define QBS_BUILDGRAPHNODE_H
+
+#include "nodeset.h"
+#include <language/forward_decls.h>
+#include <tools/persistentobject.h>
+#include <tools/weakpointer.h>
+
+namespace qbs {
+namespace Internal {
+
+class BuildGraphVisitor;
+class TopLevelProject;
+
+class BuildGraphNode : public virtual PersistentObject
+{
+ friend class NodeSet;
+public:
+ virtual ~BuildGraphNode();
+
+ NodeSet parents;
+ NodeSet children;
+ WeakPointer<ResolvedProduct> product;
+
+ enum BuildState
+ {
+ Untouched = 0,
+ Buildable,
+ Building,
+ Built
+ };
+
+ BuildState buildState; // Do not serialize. Will be refreshed for every build.
+
+ virtual void accept(BuildGraphVisitor *visitor) = 0;
+ virtual QString toString() const = 0;
+ void connectChild(BuildGraphNode *child);
+
+protected:
+ explicit BuildGraphNode();
+ void acceptChildren(BuildGraphVisitor *visitor);
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+
+ enum Type
+ {
+ ArtifactNodeType,
+ RuleNodeType,
+ PhonyTargetNodeType
+ };
+ virtual Type type() const = 0;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_BUILDGRAPHNODE_H
diff --git a/src/lib/buildgraph/buildgraphvisitor.h b/src/lib/buildgraph/buildgraphvisitor.h
new file mode 100644
index 000000000..d896d4650
--- /dev/null
+++ b/src/lib/buildgraph/buildgraphvisitor.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_BUILDGRAPHVISITOR_H
+#define QBS_BUILDGRAPHVISITOR_H
+
+namespace qbs {
+namespace Internal {
+
+class Artifact;
+class PhonyTarget;
+class RuleNode;
+
+/*!
+ * \brief The BuildGraphVisitor class
+ *
+ * The return value of a visit method indicates whether the children of the current node
+ * are to be visited next.
+ */
+class BuildGraphVisitor
+{
+public:
+ virtual bool visit(Artifact *) { return true; }
+ virtual bool visit(PhonyTarget *) { return true; }
+ virtual bool visit(RuleNode *) { return true; }
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_BUILDGRAPHVISITOR_H
diff --git a/src/lib/buildgraph/cycledetector.cpp b/src/lib/buildgraph/cycledetector.cpp
index bb5d0bf38..57bb36bb6 100644
--- a/src/lib/buildgraph/cycledetector.cpp
+++ b/src/lib/buildgraph/cycledetector.cpp
@@ -30,6 +30,7 @@
#include "artifact.h"
#include "buildgraph.h"
+#include "projectbuilddata.h"
#include <language/language.h>
#include <logging/translator.h>
@@ -39,43 +40,48 @@ namespace qbs {
namespace Internal {
CycleDetector::CycleDetector(const Logger &logger)
- : ArtifactVisitor(0), m_parent(0), m_logger(logger)
+ : m_parent(0), m_logger(logger)
{
}
-void CycleDetector::visitProject(const ResolvedProjectConstPtr &project)
+void CycleDetector::visitProject(const TopLevelProjectConstPtr &project)
{
const QString description = QString::fromLocal8Bit("Cycle detection for project '%1'")
.arg(project->name);
TimedActivityLogger timeLogger(m_logger, description, QLatin1String("[BG] "), LoggerTrace);
- ArtifactVisitor::visitProject(project);
+
+ project->accept(this);
}
-void CycleDetector::visitArtifact(Artifact *artifact)
+void CycleDetector::visitProduct(const ResolvedProductConstPtr &product)
+{
+ product->accept(this);
+}
+
+bool CycleDetector::visit(Artifact *artifact)
{
if (Q_UNLIKELY(m_artifactsInCurrentPath.contains(artifact))) {
ErrorInfo error(Tr::tr("Cycle in build graph detected."));
- foreach (const Artifact * const a, cycle(artifact))
- error.append(a->filePath());
+ foreach (const BuildGraphNode * const node, cycle(artifact))
+ error.append(node->toString());
throw error;
}
if (m_allArtifacts.contains(artifact))
- return;
+ return true;
m_artifactsInCurrentPath += artifact;
m_parent = artifact;
- foreach (Artifact * const child, artifact->children)
- visitArtifact(child);
+ foreach (BuildGraphNode * const child, artifact->children)
+ child->accept(this);
m_artifactsInCurrentPath -= artifact;
m_allArtifacts += artifact;
+ return true;
}
-void CycleDetector::doVisit(Artifact *) { }
-
-QList<Artifact *> CycleDetector::cycle(Artifact *doubleEntry)
+QList<BuildGraphNode *> CycleDetector::cycle(BuildGraphNode *doubleEntry)
{
- QList<Artifact *> path;
+ QList<BuildGraphNode *> path;
findPath(doubleEntry, m_parent, path);
return path << doubleEntry;
}
diff --git a/src/lib/buildgraph/cycledetector.h b/src/lib/buildgraph/cycledetector.h
index 61ddac0b1..654bba8f5 100644
--- a/src/lib/buildgraph/cycledetector.h
+++ b/src/lib/buildgraph/cycledetector.h
@@ -29,7 +29,8 @@
#ifndef QBS_CYCLEDETECTOR_H
#define QBS_CYCLEDETECTOR_H
-#include "artifactvisitor.h"
+#include "buildgraphvisitor.h"
+#include <language/forward_decls.h>
#include <logging/logger.h>
#include <QSet>
@@ -37,18 +38,20 @@
namespace qbs {
namespace Internal {
-class CycleDetector : public ArtifactVisitor
+class BuildGraphNode;
+
+class CycleDetector : private BuildGraphVisitor
{
public:
CycleDetector(const Logger &logger);
- void visitProject(const ResolvedProjectConstPtr &project);
- void visitArtifact(Artifact *artifact);
+ void visitProject(const TopLevelProjectConstPtr &project);
+ void visitProduct(const ResolvedProductConstPtr &product);
private:
- void doVisit(Artifact *artifact);
+ bool visit(Artifact *artifact);
- QList<Artifact *> cycle(Artifact *doubleEntry);
+ QList<BuildGraphNode *> cycle(BuildGraphNode *doubleEntry);
QSet<Artifact *> m_allArtifacts;
QSet<Artifact *> m_artifactsInCurrentPath;
diff --git a/src/lib/buildgraph/executor.cpp b/src/lib/buildgraph/executor.cpp
index 5d8ea52cf..fc80eff87 100644
--- a/src/lib/buildgraph/executor.cpp
+++ b/src/lib/buildgraph/executor.cpp
@@ -29,14 +29,16 @@
#include "executor.h"
#include "artifactvisitor.h"
-#include "automoc.h"
#include "buildgraph.h"
#include "productbuilddata.h"
#include "projectbuilddata.h"
#include "cycledetector.h"
#include "executorjob.h"
#include "inputartifactscanner.h"
+#include "phonytarget.h"
+#include "rulenode.h"
#include "rulesevaluationcontext.h"
+#include "tagscannerrunner.h"
#include <buildgraph/transformer.h>
#include <language/language.h>
@@ -91,7 +93,7 @@ private:
};
-bool Executor::ComparePriority::operator() (const Artifact *x, const Artifact *y) const
+bool Executor::ComparePriority::operator() (const BuildGraphNode *x, const BuildGraphNode *y) const
{
return x->product->buildData->buildPriority < y->product->buildData->buildPriority;
}
@@ -106,10 +108,6 @@ Executor::Executor(const Logger &logger, QObject *parent)
, m_doDebug(logger.debugEnabled())
{
m_inputArtifactScanContext = new InputArtifactScannerContext(&m_scanResultCache);
- m_autoMoc = new AutoMoc(logger);
- connect(m_autoMoc, SIGNAL(reportCommandDescription(QString,QString)),
- this, SIGNAL(reportCommandDescription(QString,QString)));
- m_autoMoc->setScanResultCache(&m_scanResultCache);
}
Executor::~Executor()
@@ -119,7 +117,6 @@ Executor::~Executor()
delete job;
foreach (ExecutorJob *job, m_processingJobs.keys())
delete job;
- delete m_autoMoc; // delete before shared scan result cache
delete m_inputArtifactScanContext;
}
@@ -225,6 +222,7 @@ void Executor::doBuild()
}
QBS_CHECK(m_state == ExecutorIdle);
m_leaves = Leaves();
+ m_changedSourceArtifacts.clear();
m_error.clear();
m_explicitlyCanceled = false;
m_activeFileTags = FileTags::fromStringList(m_buildOptions.activeFileTags());
@@ -250,8 +248,7 @@ void Executor::doBuild()
foreach (ExecutorJob * const job, m_availableJobs)
job->setDryRun(m_buildOptions.dryRun());
- bool sourceFilesChanged = false;
- prepareAllArtifacts(&sourceFilesChanged);
+ prepareAllNodes();
Artifact::BuildState initialBuildState = m_buildOptions.changedFiles().isEmpty()
? Artifact::Buildable : Artifact::Built;
@@ -281,19 +278,22 @@ void Executor::doBuild()
// find the root nodes
m_roots.clear();
foreach (const ResolvedProductPtr &product, m_productsToBuild) {
- foreach (Artifact *targetArtifact, product->buildData->targetArtifacts) {
- m_roots += targetArtifact;
-
- // The user expects that he can delete target artifacts and they get rebuilt.
- // To achieve this we must retrieve their timestamps.
- targetArtifact->setTimestamp(FileInfo(targetArtifact->filePath()).lastModified());
+ foreach (BuildGraphNode *rootNode, product->buildData->roots) {
+ m_roots += rootNode;
+ Artifact *targetArtifact = dynamic_cast<Artifact *>(rootNode);
+ if (targetArtifact && product->buildData->targetArtifacts.contains(targetArtifact)) {
+ // The user expects that he can delete target artifacts and they get rebuilt.
+ // To achieve this we must retrieve their timestamps.
+ targetArtifact->setTimestamp(FileInfo(targetArtifact->filePath()).lastModified());
+ }
}
}
prepareReachableArtifacts(initialBuildState);
- setupProgressObserver(sourceFilesChanged);
- if (sourceFilesChanged)
- runAutoMoc();
+ setupProgressObserver(!m_changedSourceArtifacts.isEmpty());
+ if (!m_changedSourceArtifacts.isEmpty()) {
+ runTagScanners();
+ }
initLeaves(changedArtifacts);
if (!scheduleJobs()) {
m_logger.qbsTrace() << "Nothing to do at all, finishing.";
@@ -306,48 +306,49 @@ void Executor::setBuildOptions(const BuildOptions &buildOptions)
m_buildOptions = buildOptions;
}
-static void initArtifactsBottomUp(Artifact *artifact)
+static void initArtifactsBottomUp(BuildGraphNode *node)
{
- if (artifact->buildState == Artifact::Untouched)
+ if (node->buildState == Artifact::Untouched)
return;
- artifact->buildState = Artifact::Buildable;
- foreach (Artifact *parent, artifact->parents)
+ node->buildState = Artifact::Buildable;
+ foreach (BuildGraphNode *parent, node->parents)
initArtifactsBottomUp(parent);
}
void Executor::initLeaves(const QList<Artifact *> &changedArtifacts)
{
if (changedArtifacts.isEmpty()) {
- QSet<Artifact *> seenArtifacts;
- foreach (Artifact *root, m_roots)
- initLeavesTopDown(root, seenArtifacts);
+ QSet<BuildGraphNode *> seenNodes;
+ foreach (BuildGraphNode *root, m_roots)
+ initLeavesTopDown(root, seenNodes);
} else {
- foreach (Artifact *artifact, changedArtifacts) {
+ foreach (BuildGraphNode *artifact, changedArtifacts) {
m_leaves.push(artifact);
initArtifactsBottomUp(artifact);
}
}
}
-void Executor::initLeavesTopDown(Artifact *artifact, QSet<Artifact *> &seenArtifacts)
+void Executor::initLeavesTopDown(BuildGraphNode *node, QSet<BuildGraphNode *> &seenNodes)
{
- if (seenArtifacts.contains(artifact))
+ if (seenNodes.contains(node))
return;
- seenArtifacts += artifact;
+ seenNodes += node;
// Artifacts that appear in the build graph after
// prepareBuildGraph() has been called, must be initialized.
- if (artifact->buildState == Artifact::Untouched) {
- artifact->buildState = Artifact::Buildable;
- if (artifact->artifactType == Artifact::SourceFile)
+ if (node->buildState == Artifact::Untouched) {
+ node->buildState = Artifact::Buildable;
+ Artifact *artifact = dynamic_cast<Artifact *>(node);
+ if (artifact && artifact->artifactType == Artifact::SourceFile)
retrieveSourceFileTimestamp(artifact);
}
- if (artifact->children.isEmpty()) {
- m_leaves.push(artifact);
+ if (node->children.isEmpty()) {
+ m_leaves.push(node);
} else {
- foreach (Artifact *child, artifact->children)
- initLeavesTopDown(child, seenArtifacts);
+ foreach (BuildGraphNode *child, node->children)
+ initLeavesTopDown(child, seenNodes);
}
}
@@ -356,11 +357,34 @@ bool Executor::scheduleJobs()
{
QBS_CHECK(m_state == ExecutorRunning);
while (!m_leaves.empty() && !m_availableJobs.isEmpty()) {
- Artifact * const artifact = m_leaves.top();
+ BuildGraphNode * const nodeToBuild = m_leaves.top();
m_leaves.pop();
- buildArtifact(artifact);
+
+ switch (nodeToBuild->buildState) {
+ case BuildGraphNode::Untouched:
+ QBS_ASSERT(!"untouched node in leaves list", /* ignore */);
+ continue;
+ case BuildGraphNode::Buildable:
+ // This is the only state in which we want to build a node.
+ break;
+ case BuildGraphNode::Building:
+ if (m_doDebug) {
+ m_logger.qbsDebug() << "[EXEC] " << nodeToBuild->toString();
+ m_logger.qbsDebug() << "[EXEC] node is currently being built. Skipping.";
+ }
+ continue;
+ case BuildGraphNode::Built:
+ if (m_doDebug) {
+ m_logger.qbsDebug() << "[EXEC] " << nodeToBuild->toString();
+ m_logger.qbsDebug() << "[EXEC] node already built. Skipping.";
+ }
+ continue;
+ }
+
+ nodeToBuild->accept(this);
}
return !m_leaves.empty() || !m_processingJobs.isEmpty();
+
}
bool Executor::isUpToDate(Artifact *artifact) const
@@ -387,11 +411,15 @@ bool Executor::isUpToDate(Artifact *artifact) const
return false;
}
- foreach (Artifact *child, artifact->children) {
- QBS_CHECK(child->timestamp().isValid());
+ foreach (BuildGraphNode *child, artifact->children) {
+ Artifact *childArtifact = dynamic_cast<Artifact *>(child);
+ if (!childArtifact)
+ continue;
+ QBS_CHECK(childArtifact->timestamp().isValid());
if (debug)
- m_logger.qbsDebug() << "[UTD] child timestamp " << child->timestamp().toString();
- if (artifact->timestamp() < child->timestamp())
+ m_logger.qbsDebug() << "[UTD] child timestamp "
+ << childArtifact->timestamp().toString();
+ if (artifact->timestamp() < childArtifact->timestamp())
return false;
}
@@ -429,13 +457,6 @@ void Executor::buildArtifact(Artifact *artifact)
if (m_doDebug)
m_logger.qbsDebug() << "[EXEC] " << relativeArtifactFileName(artifact);
- // Skip artifacts that are already built.
- if (artifact->buildState == Artifact::Built) {
- if (m_doDebug)
- m_logger.qbsDebug() << "[EXEC] artifact already built. Skipping.";
- return;
- }
-
// skip artifacts without transformer
if (artifact->artifactType != Artifact::Generated) {
// For source artifacts, that were not reachable when initializing the build, we must
@@ -495,7 +516,7 @@ void Executor::buildArtifact(Artifact *artifact)
// create the output directories
if (!m_buildOptions.dryRun()) {
- ArtifactList::const_iterator it = artifact->transformer->outputs.begin();
+ ArtifactSet::const_iterator it = artifact->transformer->outputs.begin();
for (; it != artifact->transformer->outputs.end(); ++it) {
Artifact *output = *it;
QDir outDir = QFileInfo(output->filePath()).absoluteDir();
@@ -511,8 +532,8 @@ void Executor::buildArtifact(Artifact *artifact)
// postpone the build of this artifact, if new dependencies found
if (scanner.newDependencyAdded()) {
bool buildingDependenciesFound = false;
- QVector<Artifact *> unbuiltDependencies;
- foreach (Artifact *dependency, artifact->children) {
+ QVector<BuildGraphNode *> unbuiltDependencies;
+ foreach (BuildGraphNode *dependency, artifact->children) {
switch (dependency->buildState) {
case Artifact::Untouched:
case Artifact::Buildable:
@@ -548,6 +569,59 @@ void Executor::buildArtifact(Artifact *artifact)
job->run(artifact->transformer.data(), artifact->product);
}
+void Executor::buildPhonyTarget(PhonyTarget *target)
+{
+ if (target->transformer()) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] " << target->toString();
+
+ // Skip targets that are already built.
+ if (target->buildState == Artifact::Built) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] phony target already built. Skipping.";
+ return;
+ }
+
+ } else {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] " << target->toString()
+ << " No transformer. Skipping.";
+ }
+ finishNode(target);
+}
+
+void Executor::executeRuleNode(RuleNode *ruleNode)
+{
+ RuleNode::ApplicationResult result;
+ ruleNode->apply(m_logger, &result);
+
+ if (result.upToDate) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] " << ruleNode->toString()
+ << " is up to date. Skipping.";
+ } else {
+ qDebug() << "EXEC RULE" << ruleNode->toString();
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] " << ruleNode->toString();
+ const QVector<BuildGraphNode *> &createdNodes = result.createdNodes;
+ const WeakPointer<ResolvedProduct> &product = ruleNode->product;
+ foreach (BuildGraphNode *node, createdNodes) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] rule created " << node->toString();
+ loggedConnect(node, ruleNode, m_logger);
+ Artifact *artifact = dynamic_cast<Artifact *>(node);
+ if (!artifact)
+ continue;
+ if (artifact->fileTags.matches(product->fileTags)) {
+ product->buildData->roots += artifact;
+ product->buildData->targetArtifacts += artifact;
+ }
+ }
+ insertLeavesAfterAddingDependencies(createdNodes);
+ }
+ finishNode(ruleNode);
+}
+
void Executor::finishJob(ExecutorJob *job, bool success)
{
QBS_CHECK(job);
@@ -583,27 +657,21 @@ void Executor::finishJob(ExecutorJob *job, bool success)
}
}
-static bool allChildrenBuilt(Artifact *artifact)
+static bool allChildrenBuilt(BuildGraphNode *node)
{
- foreach (Artifact *child, artifact->children)
+ foreach (BuildGraphNode *child, node->children)
if (child->buildState != Artifact::Built)
return false;
return true;
}
-void Executor::finishArtifact(Artifact *leaf)
+void Executor::finishNode(BuildGraphNode *leaf)
{
- QBS_CHECK(leaf);
-
- if (m_doTrace)
- m_logger.qbsTrace() << "[EXEC] finishArtifact " << relativeArtifactFileName(leaf);
-
leaf->buildState = Artifact::Built;
- m_scanResultCache.remove(leaf->filePath());
- foreach (Artifact *parent, leaf->parents) {
+ foreach (BuildGraphNode *parent, leaf->parents) {
if (parent->buildState != Artifact::Buildable) {
if (m_doTrace) {
- m_logger.qbsTrace() << "[EXEC] parent " << relativeArtifactFileName(parent)
+ m_logger.qbsTrace() << "[EXEC] parent " << parent->toString()
<< " build state: " << toString(parent->buildState);
}
continue;
@@ -612,16 +680,26 @@ void Executor::finishArtifact(Artifact *leaf)
if (allChildrenBuilt(parent)) {
m_leaves.push(parent);
if (m_doTrace) {
- m_logger.qbsTrace() << "[EXEC] finishArtifact adds leaf "
- << relativeArtifactFileName(parent) << " " << toString(parent->buildState);
+ m_logger.qbsTrace() << "[EXEC] finishNode adds leaf "
+ << parent->toString() << " " << toString(parent->buildState);
}
} else {
if (m_doTrace) {
- m_logger.qbsTrace() << "[EXEC] parent " << relativeArtifactFileName(parent)
+ m_logger.qbsTrace() << "[EXEC] parent " << parent->toString()
<< " build state: " << toString(parent->buildState);
}
}
}
+}
+
+void Executor::finishArtifact(Artifact *leaf)
+{
+ QBS_CHECK(leaf);
+ if (m_doTrace)
+ m_logger.qbsTrace() << "[EXEC] finishArtifact " << relativeArtifactFileName(leaf);
+
+ finishNode(leaf);
+ m_scanResultCache.remove(leaf->filePath());
if (leaf->transformer)
foreach (Artifact *sideBySideArtifact, leaf->transformer->outputs)
@@ -632,28 +710,28 @@ void Executor::finishArtifact(Artifact *leaf)
m_progressObserver->incrementProgressValue(BuildEffortCalculator::multiplier(leaf));
}
-void Executor::insertLeavesAfterAddingDependencies_recurse(Artifact *const artifact,
- QSet<Artifact *> *seenArtifacts, Leaves *leaves) const
+void Executor::insertLeavesAfterAddingDependencies_recurse(BuildGraphNode *const node,
+ QSet<BuildGraphNode *> *seenNodes, Leaves *leaves) const
{
- if (seenArtifacts->contains(artifact))
+ if (seenNodes->contains(node))
return;
- seenArtifacts->insert(artifact);
+ seenNodes->insert(node);
- if (artifact->buildState == Artifact::Untouched)
- artifact->buildState = Artifact::Buildable;
+ if (node->buildState == Artifact::Untouched)
+ node->buildState = Artifact::Buildable;
bool isLeaf = true;
- foreach (Artifact *child, artifact->children) {
+ foreach (BuildGraphNode *child, node->children) {
if (child->buildState != Artifact::Built) {
isLeaf = false;
- insertLeavesAfterAddingDependencies_recurse(child, seenArtifacts, leaves);
+ insertLeavesAfterAddingDependencies_recurse(child, seenNodes, leaves);
}
}
if (isLeaf) {
if (m_doDebug)
- m_logger.qbsDebug() << "[EXEC] adding leaf " << relativeArtifactFileName(artifact);
- leaves->push(artifact);
+ m_logger.qbsDebug() << "[EXEC] adding leaf " << node->toString();
+ leaves->push(node);
}
}
@@ -662,11 +740,11 @@ QString Executor::configString() const
return tr(" for configuration %1").arg(m_project->id());
}
-void Executor::insertLeavesAfterAddingDependencies(QVector<Artifact *> dependencies)
+void Executor::insertLeavesAfterAddingDependencies(QVector<BuildGraphNode *> dependencies)
{
- QSet<Artifact *> seenArtifacts;
- foreach (Artifact *dependency, dependencies)
- insertLeavesAfterAddingDependencies_recurse(dependency, &seenArtifacts, &m_leaves);
+ QSet<BuildGraphNode *> seenNodes;
+ foreach (BuildGraphNode *dependency, dependencies)
+ insertLeavesAfterAddingDependencies_recurse(dependency, &seenNodes, &m_leaves);
}
void Executor::cancelJobs()
@@ -731,27 +809,19 @@ void Executor::addExecutorJobs(int jobNumber)
}
}
-void Executor::runAutoMoc()
+void Executor::runTagScanners()
{
- bool autoMocApplied = false;
foreach (const ResolvedProductPtr &product, m_productsToBuild) {
if (m_progressObserver && m_progressObserver->canceled())
throw ErrorInfo(Tr::tr("Build canceled%1.").arg(configString()));
- // HACK call the automoc thingy here only if we have use Qt/core module
- foreach (const ResolvedModuleConstPtr &m, product->modules) {
- if (m->name == "Qt/core") {
- autoMocApplied = true;
- m_autoMoc->apply(product);
- break;
- }
- }
+ QHash<FileTag, QList<Artifact *> > sourceArtifacts;
+ foreach (Artifact *artifact, m_changedSourceArtifacts)
+ foreach (const FileTag &tag, artifact->fileTags)
+ sourceArtifacts[tag] += artifact;
+ foreach (const TagScannerConstPtr &rootTagScanner, product->tagScanners)
+ runTagScannersTopOrdered(&m_scanResultCache, product, rootTagScanner, sourceArtifacts,
+ m_logger);
}
- if (autoMocApplied) {
- foreach (const ResolvedProductConstPtr &product, m_productsToBuild)
- CycleDetector(m_logger).visitProduct(product);
- }
- if (m_progressObserver)
- m_progressObserver->incrementProgressValue(m_mocEffort);
}
void Executor::onProcessError(const qbs::ErrorInfo &err)
@@ -800,8 +870,8 @@ void Executor::finish()
QStringList unbuiltProductNames;
foreach (const ResolvedProductPtr &product, m_productsToBuild) {
- foreach (Artifact *artifact, product->buildData->targetArtifacts) {
- if (artifact->buildState != Artifact::Built) {
+ foreach (BuildGraphNode *rootNode, product->buildData->roots) {
+ if (rootNode->buildState != Artifact::Built) {
unbuiltProductNames += product->name;
break;
}
@@ -823,35 +893,59 @@ void Executor::finish()
emit finished();
}
+bool Executor::visit(Artifact *artifact)
+{
+ buildArtifact(artifact);
+ return false;
+}
+
+bool Executor::visit(PhonyTarget *target)
+{
+ buildPhonyTarget(target);
+ return false;
+}
+
+bool Executor::visit(RuleNode *ruleNode)
+{
+ executeRuleNode(ruleNode);
+ return false;
+}
+
/**
* Sets the state of all artifacts in the graph to "untouched".
* This must be done before doing a build.
*
* Retrieves the timestamps of source artifacts.
*
- * This function sets *sourceFilesChanged to true, if the timestamp of a reachable source artifact
- * changed.
+ * This function also fills the list of changed source files.
*/
-void Executor::prepareAllArtifacts(bool *sourceFilesChanged)
+void Executor::prepareAllNodes()
{
foreach (const ResolvedProductPtr &product, m_productsToBuild) {
- foreach (Artifact *artifact, product->buildData->artifacts) {
- artifact->buildState = Artifact::Untouched;
- artifact->inputsScanned = false;
- artifact->timestampRetrieved = false;
+ foreach (BuildGraphNode *node, product->buildData->nodes) {
+ node->buildState = Artifact::Untouched;
+ Artifact *artifact = dynamic_cast<Artifact *>(node);
+ if (artifact)
+ prepareArtifact(artifact);
+ }
+ }
+}
- if (artifact->artifactType == Artifact::SourceFile) {
- const FileTime oldTimestamp = artifact->timestamp();
- retrieveSourceFileTimestamp(artifact);
- if (oldTimestamp != artifact->timestamp())
- *sourceFilesChanged = true;
- }
+void Executor::prepareArtifact(Artifact *artifact)
+{
+ artifact->inputsScanned = false;
+ artifact->timestampRetrieved = false;
- // Timestamps of file dependencies must be invalid for every build.
- foreach (FileDependency *fileDependency, artifact->fileDependencies)
- fileDependency->clearTimestamp();
- }
+ if (artifact->artifactType == Artifact::SourceFile) {
+ const FileTime oldTimestamp = artifact->timestamp();
+ retrieveSourceFileTimestamp(artifact);
+ if (oldTimestamp != artifact->timestamp())
+ m_changedSourceArtifacts.append(artifact);
}
+
+ // Timestamps of file dependencies must be invalid for every build.
+ foreach (FileDependency *fileDependency, artifact->fileDependencies)
+ fileDependency->clearTimestamp();
}
/**
@@ -860,38 +954,39 @@ void Executor::prepareAllArtifacts(bool *sourceFilesChanged)
*/
void Executor::prepareReachableArtifacts(const Artifact::BuildState buildState)
{
- foreach (Artifact *root, m_roots)
+ foreach (BuildGraphNode *root, m_roots)
prepareReachableArtifacts_impl(root, buildState);
}
-void Executor::prepareReachableArtifacts_impl(Artifact *artifact,
+void Executor::prepareReachableArtifacts_impl(BuildGraphNode *node,
const Artifact::BuildState buildState)
{
- if (artifact->buildState != Artifact::Untouched)
+ if (node->buildState != Artifact::Untouched)
return;
- artifact->buildState = buildState;
- foreach (Artifact *child, artifact->children)
+ node->buildState = buildState;
+ foreach (BuildGraphNode *child, node->children)
prepareReachableArtifacts_impl(child, buildState);
}
void Executor::updateBuildGraph(Artifact::BuildState buildState)
{
- QSet<Artifact *> seenArtifacts;
- foreach (Artifact *root, m_roots)
+ QSet<BuildGraphNode *> seenArtifacts;
+ foreach (BuildGraphNode *root, m_roots)
updateBuildGraph_impl(root, buildState, seenArtifacts);
}
-void Executor::updateBuildGraph_impl(Artifact *artifact, Artifact::BuildState buildState, QSet<Artifact *> &seenArtifacts)
+void Executor::updateBuildGraph_impl(BuildGraphNode *node, Artifact::BuildState buildState,
+ QSet<BuildGraphNode *> &seenNodes)
{
- if (seenArtifacts.contains(artifact))
+ if (seenNodes.contains(node))
return;
- seenArtifacts += artifact;
- artifact->buildState = buildState;
+ seenNodes += node;
+ node->buildState = buildState;
- foreach (Artifact *child, artifact->children)
- updateBuildGraph_impl(child, buildState, seenArtifacts);
+ foreach (BuildGraphNode *child, node->children)
+ updateBuildGraph_impl(child, buildState, seenNodes);
}
void Executor::setState(ExecutorState s)
diff --git a/src/lib/buildgraph/executor.h b/src/lib/buildgraph/executor.h
index 00f2e0124..94cbb164b 100644
--- a/src/lib/buildgraph/executor.h
+++ b/src/lib/buildgraph/executor.h
@@ -31,6 +31,7 @@
#define QBS_BUILDGRAPHEXECUTOR_H
#include "forward_decls.h"
+#include "buildgraphvisitor.h"
#include <buildgraph/artifact.h>
#include <buildgraph/scanresultcache.h>
#include <language/forward_decls.h>
@@ -46,13 +47,14 @@ namespace qbs {
class ProcessResult;
namespace Internal {
-class AutoMoc;
+class MocTagScanner;
class ExecutorJob;
class FileTime;
class InputArtifactScannerContext;
class ProgressObserver;
+class RuleNode;
-class Executor : public QObject
+class Executor : public QObject, private BuildGraphVisitor
{
Q_OBJECT
@@ -82,42 +84,51 @@ private slots:
void finish();
private:
+ // BuildGraphVisitor implementation
+ bool visit(Artifact *artifact);
+ bool visit(PhonyTarget *target);
+ bool visit(RuleNode *ruleNode);
+
enum ExecutorState { ExecutorIdle, ExecutorRunning, ExecutorCanceling };
struct ComparePriority
{
- bool operator() (const Artifact *x, const Artifact *y) const;
+ bool operator() (const BuildGraphNode *x, const BuildGraphNode *y) const;
};
- typedef std::priority_queue<Artifact *, std::vector<Artifact *>, ComparePriority> Leaves;
+ typedef std::priority_queue<Artifact *, std::vector<BuildGraphNode *>, ComparePriority> Leaves;
void doBuild();
- void prepareAllArtifacts(bool *sourceFilesChanged);
+ void prepareAllNodes();
+ void prepareArtifact(Artifact *artifact);
void prepareReachableArtifacts(const Artifact::BuildState buildState);
- void prepareReachableArtifacts_impl(Artifact *artifact, const Artifact::BuildState buildState);
+ void prepareReachableArtifacts_impl(BuildGraphNode *node, const Artifact::BuildState buildState);
void updateBuildGraph(Artifact::BuildState buildState);
- void updateBuildGraph_impl(Artifact *artifact, Artifact::BuildState buildState, QSet<Artifact *> &seenArtifacts);
+ void updateBuildGraph_impl(BuildGraphNode *node, Artifact::BuildState buildState, QSet<BuildGraphNode *> &seenNodes);
void initLeaves(const QList<Artifact *> &changedArtifacts);
- void initLeavesTopDown(Artifact *artifact, QSet<Artifact *> &seenArtifacts);
+ void initLeavesTopDown(BuildGraphNode *artifact, QSet<BuildGraphNode *> &seenArtifacts);
bool scheduleJobs();
void buildArtifact(Artifact *artifact);
+ void buildPhonyTarget(PhonyTarget *target);
+ void executeRuleNode(RuleNode *ruleNode);
void finishJob(ExecutorJob *job, bool success);
+ void finishNode(BuildGraphNode *leaf);
void finishArtifact(Artifact *artifact);
void setState(ExecutorState);
void addExecutorJobs(int jobNumber);
- void runAutoMoc();
- void insertLeavesAfterAddingDependencies(QVector<Artifact *> dependencies);
+ void insertLeavesAfterAddingDependencies(QVector<BuildGraphNode *> dependencies);
void cancelJobs();
void setupProgressObserver(bool mocWillRun);
void doSanityChecks();
void handleError(const ErrorInfo &error);
+ void runTagScanners();
bool mustExecuteTransformer(const TransformerPtr &transformer) const;
bool isUpToDate(Artifact *artifact) const;
void retrieveSourceFileTimestamp(Artifact *artifact) const;
FileTime recursiveFileTime(const QString &filePath) const;
- void insertLeavesAfterAddingDependencies_recurse(Artifact *const artifact,
- QSet<Artifact *> *seenArtifacts, Leaves *leaves) const;
+ void insertLeavesAfterAddingDependencies_recurse(BuildGraphNode * const node,
+ QSet<BuildGraphNode *> *seenNodes, Leaves *leaves) const;
QString configString() const;
RulesEvaluationContextPtr m_evalContext;
@@ -129,11 +140,11 @@ private:
ExecutorState m_state;
TopLevelProjectPtr m_project;
QList<ResolvedProductPtr> m_productsToBuild;
- QList<Artifact *> m_roots;
+ QList<BuildGraphNode *> m_roots;
Leaves m_leaves;
+ QList<Artifact *> m_changedSourceArtifacts;
ScanResultCache m_scanResultCache;
InputArtifactScannerContext *m_inputArtifactScanContext;
- AutoMoc *m_autoMoc;
int m_mocEffort;
ErrorInfo m_error;
bool m_explicitlyCanceled;
diff --git a/src/lib/buildgraph/inputartifactscanner.cpp b/src/lib/buildgraph/inputartifactscanner.cpp
index f40e378fe..9c357e8e5 100644
--- a/src/lib/buildgraph/inputartifactscanner.cpp
+++ b/src/lib/buildgraph/inputartifactscanner.cpp
@@ -161,9 +161,10 @@ void InputArtifactScanner::scan()
// Remove all connections to children that were added by the dependency scanner.
// They will be regenerated.
foreach (Artifact *dependency, m_artifact->childrenAddedByScanner)
+ if (!dependency || m_artifact->transformer->inputs.contains(dependency))
disconnect(m_artifact, dependency, m_logger);
- ArtifactList::const_iterator it = m_artifact->transformer->inputs.begin();
+ ArtifactSet::const_iterator it = m_artifact->transformer->inputs.begin();
for (; it != m_artifact->transformer->inputs.end(); ++it) {
Artifact *inputArtifact = *it;
QStringList includePaths;
@@ -352,7 +353,7 @@ void InputArtifactScanner::handleDependency(ResolvedDependency &dependency)
} else {
if (m_artifact->children.contains(artifactDependency))
return;
- if (insertIntoProduct && !product->buildData->artifacts.contains(artifactDependency))
+ if (insertIntoProduct && !product->buildData->nodes.contains(artifactDependency))
insertArtifact(product, artifactDependency, m_logger);
safeConnect(m_artifact, artifactDependency, m_logger);
m_artifact->childrenAddedByScanner += artifactDependency;
diff --git a/src/lib/buildgraph/moctagscanner.cpp b/src/lib/buildgraph/moctagscanner.cpp
new file mode 100644
index 000000000..ca55b4de3
--- /dev/null
+++ b/src/lib/buildgraph/moctagscanner.cpp
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "moctagscanner.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
+#include "buildgraph.h"
+#include "rulesapplicator.h"
+#include "scanresultcache.h"
+#include <buildgraph/artifact.h>
+#include <buildgraph/transformer.h>
+#include <language/language.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/scannerpluginmanager.h>
+
+#include <QScriptValueIterator>
+
+namespace qbs {
+namespace Internal {
+
+MocTagScanner::MocTagScanner(FileType fileType, QScriptValue &context,
+ ScanResultCache *scanResultCache, const Logger &logger)
+ : m_fileType(fileType)
+ , m_context(context)
+ , m_scanResultCache(scanResultCache)
+ , m_logger(logger)
+{
+ if (fileType == CppFileType) {
+ m_includedMocCppFiles = m_context.engine()->newObject();
+ QScriptValue thisObj = context.property(QLatin1String("QtMocCppScanner"));
+ QBS_ASSERT(thisObj.isObject(), /* ignore */);
+ thisObj.setProperty(QLatin1String("includedMocCppFiles"), m_includedMocCppFiles);
+ } else {
+ QScriptValue mocCppScannerObj = context.property(QLatin1String("QtMocCppScanner"));
+ if (mocCppScannerObj.isObject())
+ m_includedMocCppFiles = mocCppScannerObj.property(QLatin1String("includedMocCppFiles"));
+ }
+}
+
+MocTagScanner::~MocTagScanner()
+{
+ if (m_fileType == HppFileType && m_includedMocCppFiles.isObject()) {
+ QScriptValueIterator svit(m_includedMocCppFiles);
+ while (svit.hasNext()) {
+ svit.next();
+ const QString mocCppName = QLatin1String("moc_") + svit.name() + QLatin1String(".cpp");
+ const QString includingCpp = svit.value().toString();
+ m_logger.qbsWarning()
+ << Tr::tr("Include '%1' found in '%2', "
+ "but no Q_OBJECT will generate this file.").arg(mocCppName,
+ includingCpp);
+ }
+ }
+}
+
+FileTags MocTagScanner::apply(const Artifact *artifact)
+{
+ if (cppScanners().isEmpty() || hppScanners().isEmpty())
+ throw ErrorInfo("C++ scanner cannot be loaded.");
+
+ FileTags fileTags;
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[MocTagScanner] scans " << relativeArtifactFileName(artifact);
+
+ fileTags = scan(artifact);
+ return fileTags;
+}
+
+QString MocTagScanner::generateMocFileName(Artifact *artifact, FileType fileType)
+{
+ QString mocFileName;
+ switch (fileType) {
+ case HppFileType:
+ mocFileName = "moc_" + FileInfo::baseName(artifact->filePath()) + ".cpp";
+ break;
+ case CppFileType:
+ mocFileName = FileInfo::baseName(artifact->filePath()) + ".moc";
+ break;
+ }
+ return mocFileName;
+}
+
+FileTags MocTagScanner::scan(const Artifact *artifact)
+{
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[MocTagScanner] checks " << relativeArtifactFileName(artifact);
+
+ FileTags result;
+ foreach (ScannerPlugin *scanner, m_fileType == HppFileType ? hppScanners() : cppScanners()) {
+ ScanResultCache::Result scanResult = m_scanResultCache->value(artifact->filePath());
+ if (!scanResult.valid) {
+ scanResult.valid = true;
+ void *opaq = scanner->open(artifact->filePath().utf16(),
+ ScanForDependenciesFlag | ScanForFileTagsFlag);
+ if (!opaq || !scanner->additionalFileTags)
+ continue;
+
+ int length = 0;
+ const char **szFileTagsFromScanner = scanner->additionalFileTags(opaq, &length);
+ if (szFileTagsFromScanner && length > 0) {
+ for (int i = length; --i >= 0;)
+ scanResult.additionalFileTags += szFileTagsFromScanner[i];
+ }
+
+ forever {
+ int flags = 0;
+ const char *szOutFilePath = scanner->next(opaq, &length, &flags);
+ if (szOutFilePath == 0)
+ break;
+ QString includedFilePath = QString::fromLocal8Bit(szOutFilePath, length);
+ if (includedFilePath.isEmpty())
+ continue;
+ bool isLocalInclude = (flags & SC_LOCAL_INCLUDE_FLAG);
+ scanResult.deps += ScanResultCache::Dependency(includedFilePath, isLocalInclude);
+ }
+
+ scanner->close(opaq);
+ m_scanResultCache->insert(artifact->filePath(), scanResult);
+ }
+
+ foreach (const FileTag &tag, scanResult.additionalFileTags)
+ result += tag;
+
+ foreach (const ScanResultCache::Dependency &dependency, scanResult.deps) {
+ QString includedFilePath = dependency.filePath();
+ if (includedFilePath.startsWith("moc_") && includedFilePath.endsWith(".cpp")) {
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[MocTagScanner] finds included file: "
+ << includedFilePath;
+ includedFilePath.remove(0, 4);
+ includedFilePath.chop(4);
+ m_includedMocCppFiles.setProperty(includedFilePath, true);
+ }
+ }
+
+ if (m_fileType == HppFileType && result.contains("moc_hpp")
+ && m_includedMocCppFiles.isObject()) {
+ const QString hppBaseName = FileInfo::completeBaseName(artifact->fileName());
+ if (!m_includedMocCppFiles.property(hppBaseName).isUndefined()) {
+ result.remove("moc_hpp");
+ result.insert("moc_hpp_inc");
+ m_includedMocCppFiles.setProperty(hppBaseName, QScriptValue());
+ }
+ }
+ }
+ return result;
+}
+
+const QList<ScannerPlugin *> &MocTagScanner::cppScanners() const
+{
+ if (m_cppScanners.isEmpty())
+ m_cppScanners = ScannerPluginManager::scannersForFileTag("cpp");
+
+ return m_cppScanners;
+}
+
+const QList<ScannerPlugin *> &MocTagScanner::hppScanners() const
+{
+ if (m_hppScanners.isEmpty())
+ m_hppScanners = ScannerPluginManager::scannersForFileTag("hpp");
+
+ return m_hppScanners;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/buildgraph/automoc.h b/src/lib/buildgraph/moctagscanner.h
index 118202b17..fe880a185 100644
--- a/src/lib/buildgraph/automoc.h
+++ b/src/lib/buildgraph/moctagscanner.h
@@ -32,10 +32,12 @@
#include "forward_decls.h"
+#include <language/filetags.h>
#include <language/forward_decls.h>
#include <logging/logger.h>
-#include <QObject>
+#include <QSet>
+#include <QScriptValue>
struct ScannerPlugin;
@@ -45,46 +47,37 @@ class FileTag;
class ScanResultCache;
/**
- * Scans cpp and hpp files for the Q_OBJECT / Q_GADGET macro and
- * applies the corresponding rule then.
- * Also scans the files for moc_XXX.cpp files to find out if we must
+ * Scans cpp and hpp files for the Q_OBJECT / Q_GADGET macro.
+ * Also scans the files for moc_XXX.cpp includes to find out whether we have to
* compile and link a moc_XXX.cpp file or not.
- *
- * This whole thing is an ugly hack, I know.
+ * Adds or removes the filetags moc_cpp, moc_hpp and moc_hpp_inc.
*/
-class AutoMoc : public QObject
+class MocTagScanner
{
- Q_OBJECT
-
public:
- AutoMoc(const Logger &logger, QObject *parent = 0);
-
- void setScanResultCache(ScanResultCache *scanResultCache);
- void apply(const ResolvedProductPtr &product);
-
-signals:
- void reportCommandDescription(const QString &highlight, const QString &message);
-
-private:
enum FileType
{
- UnknownFileType,
HppFileType,
CppFileType
};
+ MocTagScanner(FileType fileType, QScriptValue &context, ScanResultCache *scanResultCache,
+ const Logger &logger);
+ ~MocTagScanner();
+
+ FileTags apply(const Artifact *artifact);
+
private:
static QString generateMocFileName(Artifact *artifact, FileType fileType);
- static FileType fileType(Artifact *artifact);
- void scan(Artifact *artifact, FileType fileType, bool &hasQObjectMacro,
- QSet<QString> &includedMocCppFiles);
- bool isVictimOfMoc(Artifact *artifact, FileType fileType, FileTag &foundMocFileTag);
- void unmoc(Artifact *artifact, const FileTag &mocFileTag);
+ FileTags scan(const Artifact *artifact);
const QList<ScannerPlugin *> &cppScanners() const;
const QList<ScannerPlugin *> &hppScanners() const;
mutable QList<ScannerPlugin *> m_cppScanners;
mutable QList<ScannerPlugin *> m_hppScanners;
+ const FileType m_fileType;
+ QScriptValue m_context;
+ QScriptValue m_includedMocCppFiles;
ScanResultCache *m_scanResultCache;
Logger m_logger;
};
diff --git a/src/lib/buildgraph/nodeset.cpp b/src/lib/buildgraph/nodeset.cpp
new file mode 100644
index 000000000..e09ebbda8
--- /dev/null
+++ b/src/lib/buildgraph/nodeset.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "nodeset.h"
+
+#include "artifact.h"
+#include "phonytarget.h"
+#include "rulenode.h"
+#include <language/language.h> // because of RulePtr
+#include <tools/persistence.h>
+#include <tools/qbsassert.h>
+
+namespace qbs {
+namespace Internal {
+
+NodeSet::NodeSet()
+{
+}
+
+NodeSet::NodeSet(const NodeSet &other)
+ : m_data(other.m_data)
+{
+}
+
+NodeSet &NodeSet::unite(const NodeSet &other)
+{
+ std::set<BuildGraphNode *>::const_iterator it = other.m_data.begin();
+ for (; it != other.m_data.end(); ++it)
+ m_data.insert(*it);
+ return *this;
+}
+
+void NodeSet::remove(BuildGraphNode *node)
+{
+ iterator it = m_data.find(node);
+ if (it != m_data.end())
+ m_data.erase(it);
+}
+
+void NodeSet::load(PersistentPool &pool)
+{
+ clear();
+ int i;
+ pool.stream() >> i;
+ for (; --i >= 0;) {
+ int t;
+ pool.stream() >> t;
+ BuildGraphNode *node = 0;
+ switch (static_cast<BuildGraphNode::Type>(t)) {
+ case BuildGraphNode::ArtifactNodeType:
+ node = pool.idLoad<Artifact>();
+ break;
+ case BuildGraphNode::PhonyTargetNodeType:
+ node = pool.idLoad<PhonyTarget>();
+ break;
+ case BuildGraphNode::RuleNodeType:
+ node = pool.idLoad<RuleNode>();
+ break;
+ }
+ QBS_CHECK(node);
+ insert(node);
+ }
+}
+
+void NodeSet::store(PersistentPool &pool) const
+{
+ pool.stream() << count();
+ for (NodeSet::const_iterator it = constBegin(); it != constEnd(); ++it) {
+ pool.stream() << int((*it)->type());
+ pool.store(*it);
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/buildgraph/artifactlist.h b/src/lib/buildgraph/nodeset.h
index ffbe72ba6..98b754dff 100644
--- a/src/lib/buildgraph/artifactlist.h
+++ b/src/lib/buildgraph/nodeset.h
@@ -27,8 +27,8 @@
**
****************************************************************************/
-#ifndef QBS_ARTIFACTLIST_H
-#define QBS_ARTIFACTLIST_H
+#ifndef QBS_NODESET_H
+#define QBS_NODESET_H
#include <set>
#include <cstddef>
@@ -36,23 +36,24 @@
namespace qbs {
namespace Internal {
-class Artifact;
+class BuildGraphNode;
+class PersistentPool;
/**
- * List that holds a bunch of build graph artifacts.
+ * Set of build graph nodes.
* This is faster than QSet when iterating over the container.
*/
-class ArtifactList
+class NodeSet
{
public:
- ArtifactList();
- ArtifactList(const ArtifactList &other);
+ NodeSet();
+ NodeSet(const NodeSet &other);
- ArtifactList &unite(const ArtifactList &other);
+ NodeSet &unite(const NodeSet &other);
- typedef std::set<Artifact *>::const_iterator const_iterator;
- typedef std::set<Artifact *>::iterator iterator;
- typedef Artifact * value_type;
+ typedef std::set<BuildGraphNode *>::const_iterator const_iterator;
+ typedef std::set<BuildGraphNode *>::iterator iterator;
+ typedef BuildGraphNode * value_type;
iterator begin() { return m_data.begin(); }
iterator end() { return m_data.end(); }
@@ -61,21 +62,21 @@ public:
const_iterator constBegin() const { return m_data.begin(); }
const_iterator constEnd() const { return m_data.end(); }
- void insert(Artifact *artifact)
+ void insert(BuildGraphNode *node)
{
- m_data.insert(artifact);
+ m_data.insert(node);
}
- void operator +=(Artifact *artifact)
+ void operator +=(BuildGraphNode *node)
{
- insert(artifact);
+ insert(node);
}
- void remove(Artifact *artifact);
+ void remove(BuildGraphNode *node);
- bool contains(Artifact *artifact) const
+ bool contains(BuildGraphNode *node) const
{
- return m_data.find(artifact) != m_data.end();
+ return m_data.find(node) != m_data.end();
}
void clear()
@@ -98,15 +99,18 @@ public:
// no-op
}
- bool operator==(const ArtifactList &other) const { return m_data == other.m_data; }
- bool operator!=(const ArtifactList &other) const { return !(*this == other); }
+ bool operator==(const NodeSet &other) const { return m_data == other.m_data; }
+ bool operator!=(const NodeSet &other) const { return !(*this == other); }
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
private:
- std::set<Artifact *> m_data;
+ std::set<BuildGraphNode *> m_data;
};
} // namespace Internal
} // namespace qbs
-#endif // QBS_ARTIFACTLIST_H
+#endif // QBS_NODESET_H
diff --git a/src/lib/buildgraph/phonytarget.cpp b/src/lib/buildgraph/phonytarget.cpp
new file mode 100644
index 000000000..be8c0367a
--- /dev/null
+++ b/src/lib/buildgraph/phonytarget.cpp
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "phonytarget.h"
+
+#include "buildgraphvisitor.h"
+#include "transformer.h"
+#include <tools/persistence.h>
+
+namespace qbs {
+namespace Internal {
+
+PhonyTarget::PhonyTarget()
+{
+}
+
+PhonyTarget::~PhonyTarget()
+{
+}
+
+void PhonyTarget::setTransformer(const TransformerPtr &t)
+{
+ m_transformer = t;
+}
+
+const TransformerPtr &PhonyTarget::transformer() const
+{
+ return m_transformer;
+}
+
+void PhonyTarget::accept(BuildGraphVisitor *visitor)
+{
+ if (visitor->visit(this))
+ acceptChildren(visitor);
+}
+
+QString PhonyTarget::toString() const
+{
+ return QLatin1String("PHONYTARGET ") + m_name;
+}
+
+void PhonyTarget::load(PersistentPool &pool)
+{
+ BuildGraphNode::load(pool);
+ m_name = pool.idLoadString();
+ m_transformer = pool.idLoadS<Transformer>();
+}
+
+void PhonyTarget::store(PersistentPool &pool) const
+{
+ BuildGraphNode::store(pool);
+ pool.storeString(m_name);
+ pool.store(m_transformer);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/buildgraph/phonytarget.h b/src/lib/buildgraph/phonytarget.h
new file mode 100644
index 000000000..a457857ef
--- /dev/null
+++ b/src/lib/buildgraph/phonytarget.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_PHONYTARGET_H
+#define QBS_PHONYTARGET_H
+
+#include "buildgraphnode.h"
+#include "forward_decls.h"
+
+namespace qbs {
+namespace Internal {
+
+class PhonyTarget : public BuildGraphNode
+{
+public:
+ PhonyTarget();
+ ~PhonyTarget();
+
+ void setName(const QString &name) { m_name = name; }
+ const QString &name() const { return m_name; }
+
+ void setTransformer(const TransformerPtr &t);
+ const TransformerPtr &transformer() const;
+
+ void accept(BuildGraphVisitor *visitor);
+ QString toString() const;
+
+protected:
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+ Type type() const { return PhonyTargetNodeType; }
+
+private:
+ QString m_name;
+ TransformerPtr m_transformer;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_PHONYTARGET_H
diff --git a/src/lib/buildgraph/productbuilddata.cpp b/src/lib/buildgraph/productbuilddata.cpp
index a8d0841a8..e29ae96a5 100644
--- a/src/lib/buildgraph/productbuilddata.cpp
+++ b/src/lib/buildgraph/productbuilddata.cpp
@@ -41,18 +41,52 @@ namespace Internal {
ProductBuildData::~ProductBuildData()
{
- qDeleteAll(artifacts);
+ qDeleteAll(nodes);
+}
+
+void ProductBuildData::registerAddedFileTag(const FileTag &fileTag, Artifact *artifact)
+{
+ m_addedArtifactsByFileTag[fileTag].insert(artifact);
+}
+
+void ProductBuildData::registerAddedArtifact(Artifact *artifact)
+{
+ foreach (const FileTag &tag, artifact->fileTags)
+ m_addedArtifactsByFileTag[tag].insert(artifact);
+}
+
+void ProductBuildData::registerRemovedFileTag(const FileTag &fileTag, Artifact *artifact)
+{
+ m_removedArtifactsByFileTag[fileTag].insert(artifact);
+}
+
+void ProductBuildData::registerRemovedArtifact(Artifact *artifact)
+{
+ foreach (const FileTag &tag, artifact->fileTags)
+ m_removedArtifactsByFileTag[tag].insert(artifact);
+}
+
+const ArtifactSet ProductBuildData::addedArtifactsByFileTag(const FileTag &tag) const
+{
+ return m_addedArtifactsByFileTag.value(tag);
+}
+
+const ArtifactSet ProductBuildData::removedArtifactsByFileTag(const FileTag &tag) const
+{
+ return m_removedArtifactsByFileTag.value(tag);
}
void ProductBuildData::load(PersistentPool &pool)
{
- pool.loadContainer(artifacts);
+ nodes.load(pool);
+ roots.load(pool);
pool.loadContainer(targetArtifacts);
}
void ProductBuildData::store(PersistentPool &pool) const
{
- pool.storeContainer(artifacts);
+ nodes.store(pool);
+ roots.store(pool);
pool.storeContainer(targetArtifacts);
}
diff --git a/src/lib/buildgraph/productbuilddata.h b/src/lib/buildgraph/productbuilddata.h
index 53d68ad0d..533f9ebaa 100644
--- a/src/lib/buildgraph/productbuilddata.h
+++ b/src/lib/buildgraph/productbuilddata.h
@@ -29,9 +29,10 @@
#ifndef QBS_PRODUCTBUILDDATA_H
#define QBS_PRODUCTBUILDDATA_H
-#include "artifactlist.h"
+#include "artifactset.h"
+#include "nodeset.h"
+#include <language/filetags.h>
#include <language/forward_decls.h>
-
#include <tools/persistentobject.h>
#include <QList>
@@ -39,6 +40,7 @@
namespace qbs {
namespace Internal {
+
class Logger;
class ProductBuildData : public PersistentObject
@@ -46,15 +48,34 @@ class ProductBuildData : public PersistentObject
public:
~ProductBuildData();
- QSet<Artifact *> targetArtifacts;
- ArtifactList artifacts;
+ ArtifactSet targetArtifacts;
+ NodeSet nodes;
+ NodeSet roots;
QList<RuleConstPtr> topSortedRules;
// Do not store, initialized in executor. Higher prioritized artifacts are built first.
unsigned int buildPriority;
+ void registerAddedFileTag(const FileTag &fileTag, Artifact *artifact);
+ void registerAddedArtifact(Artifact *artifact);
+ void registerRemovedFileTag(const FileTag &fileTag, Artifact *artifact);
+ void registerRemovedArtifact(Artifact *artifact);
+ const ArtifactSet addedArtifactsByFileTag(const FileTag &tag) const;
+ const ArtifactSet removedArtifactsByFileTag(const FileTag &tag) const;
+
+ // ### HACK
+ QHash<FileTag, ArtifactSet> addedArtifacts() const { return m_addedArtifactsByFileTag; }
+ void setAddedArtifacts(const QHash<FileTag, ArtifactSet> &asfdjkl) { m_addedArtifactsByFileTag = asfdjkl; }
+ // ### END OF HACK
+
+
void load(PersistentPool &pool);
void store(PersistentPool &pool) const;
+
+private:
+ typedef QHash<FileTag, ArtifactSet> ArtifactSetByFileTag;
+ ArtifactSetByFileTag m_addedArtifactsByFileTag;
+ ArtifactSetByFileTag m_removedArtifactsByFileTag;
};
} // namespace Internal
diff --git a/src/lib/buildgraph/productinstaller.cpp b/src/lib/buildgraph/productinstaller.cpp
index 3c485dc39..583dedbd8 100644
--- a/src/lib/buildgraph/productinstaller.cpp
+++ b/src/lib/buildgraph/productinstaller.cpp
@@ -82,7 +82,8 @@ void ProductInstaller::install()
QList<const Artifact *> artifactsToInstall;
foreach (const ResolvedProductConstPtr &product, m_products) {
QBS_CHECK(product->buildData);
- foreach (const Artifact *artifact, product->buildData->artifacts) {
+ foreach (const BuildGraphNode *node, product->buildData->nodes) {
+ const Artifact *artifact = dynamic_cast<const Artifact *>(node);
if (artifact->properties->qbsPropertyValue(QLatin1String("install")).toBool())
artifactsToInstall += artifact;
}
diff --git a/src/lib/buildgraph/projectbuilddata.cpp b/src/lib/buildgraph/projectbuilddata.cpp
index 57d7ea6e0..f3d4d84a0 100644
--- a/src/lib/buildgraph/projectbuilddata.cpp
+++ b/src/lib/buildgraph/projectbuilddata.cpp
@@ -32,6 +32,9 @@
#include "buildgraph.h"
#include "productbuilddata.h"
#include "command.h"
+#include "phonytarget.h"
+#include "rulegraph.h"
+#include "rulenode.h"
#include "rulesapplicator.h"
#include "rulesevaluationcontext.h"
#include "transformer.h"
@@ -110,7 +113,7 @@ static void disconnectArtifactChildren(Artifact *artifact, const Logger &logger)
logger.qbsTrace() << QString::fromLocal8Bit("[BG] disconnectChildren: '%1'")
.arg(relativeArtifactFileName(artifact));
}
- foreach (Artifact * const child, artifact->children)
+ foreach (BuildGraphNode * const child, artifact->children)
child->parents.remove(artifact);
artifact->children.clear();
artifact->childrenAddedByScanner.clear();
@@ -123,12 +126,13 @@ static void disconnectArtifactParents(Artifact *artifact, ProjectBuildData *proj
logger.qbsTrace() << QString::fromLocal8Bit("[BG] disconnectParents: '%1'")
.arg(relativeArtifactFileName(artifact));
}
- foreach (Artifact * const parent, artifact->parents) {
+ foreach (BuildGraphNode * const parent, artifact->parents) {
parent->children.remove(artifact);
- parent->childrenAddedByScanner.remove(artifact);
- if (parent->transformer) {
- parent->transformer->inputs.remove(artifact);
- projectBuildData->artifactsThatMustGetNewTransformers += parent;
+ Artifact *parentArtifact = dynamic_cast<Artifact *>(parent);
+ if (parentArtifact->transformer) {
+ parentArtifact->childrenAddedByScanner.remove(artifact);
+ parentArtifact->transformer->inputs.remove(artifact);
+ projectBuildData->artifactsThatMustGetNewTransformers += parentArtifact;
}
}
@@ -149,11 +153,11 @@ static void disconnectArtifact(Artifact *artifact, ProjectBuildData *projectBuil
*/
void ProjectBuildData::removeArtifactAndExclusiveDependents(Artifact *artifact,
const Logger &logger, bool removeFromProduct,
- ArtifactList *removedArtifacts)
+ ArtifactSet *removedArtifacts)
{
if (removedArtifacts)
removedArtifacts->insert(artifact);
- foreach (Artifact *parent, artifact->parents) {
+ foreach (Artifact *parent, ArtifactSet::fromNodeSet(artifact->parents)) {
bool removeParent = false;
disconnect(parent, artifact, logger);
if (parent->children.isEmpty()) {
@@ -182,7 +186,7 @@ void ProjectBuildData::removeArtifact(Artifact *artifact,
removeGeneratedArtifactFromDisk(artifact, logger);
removeFromLookupTable(artifact);
if (removeFromProduct) {
- artifact->product->buildData->artifacts.remove(artifact);
+ artifact->product->buildData->nodes.remove(artifact);
artifact->product->buildData->targetArtifacts.remove(artifact);
}
disconnectArtifact(artifact, this, logger);
@@ -208,7 +212,6 @@ void ProjectBuildData::updateNodeThatMustGetNewTransformer(Artifact *artifact, c
}
removeGeneratedArtifactFromDisk(artifact, logger);
- artifact->autoMocTimestamp.clear();
artifact->clearTimestamp();
const RuleConstPtr rule = artifact->transformer->rule;
@@ -219,9 +222,10 @@ void ProjectBuildData::updateNodeThatMustGetNewTransformer(Artifact *artifact, c
sibling->transformer.clear();
ArtifactsPerFileTagMap artifactsPerFileTag;
- foreach (Artifact *input, artifact->children) {
- foreach (const FileTag &fileTag, input->fileTags)
- artifactsPerFileTag[fileTag] += input;
+ foreach (BuildGraphNode *input, artifact->children) {
+ if (Artifact *inputArtifact = dynamic_cast<Artifact *>(input))
+ foreach (const FileTag &fileTag, inputArtifact->fileTags)
+ artifactsPerFileTag[fileTag] += inputArtifact;
}
RulesApplicator rulesApplier(artifact->product, artifactsPerFileTag, logger);
rulesApplier.applyRule(rule);
@@ -278,6 +282,40 @@ void BuildDataResolver::resolveProductBuildDataForExistingProject(const TopLevel
}
}
+class CreateRuleNodes : public RuleGraphVisitor
+{
+public:
+ CreateRuleNodes(const ResolvedProductPtr &aProduct, Logger aLogger)
+ : product(aProduct), logger(aLogger)
+ {
+ }
+
+private:
+ ResolvedProductPtr product;
+ Logger logger;
+ QHash<RuleConstPtr, RuleNode *> nodePerRule;
+
+ void visit(const RuleConstPtr &parentRule, const RuleConstPtr &rule)
+ {
+ RuleNode *node = nodePerRule.value(rule);
+ if (!node) {
+ node = new RuleNode;
+ nodePerRule.insert(rule, node);
+ node->product = product;
+ node->setRule(rule);
+ product->buildData->nodes += node;
+ qDebug() << "CREATE" << node->toString();
+ }
+ if (parentRule) {
+ RuleNode *parent = nodePerRule.value(parentRule);
+ QBS_CHECK(parent);
+ loggedConnect(parent, node, logger);
+ } else {
+ product->buildData->roots += node;
+ }
+ }
+};
+
void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &product)
{
if (product->buildData)
@@ -307,6 +345,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc
}
qbsFileArtifact->fileTags.insert("qbs");
artifactsPerFileTag["qbs"].insert(qbsFileArtifact);
+ product->buildData->registerAddedArtifact(qbsFileArtifact);
// read sources
foreach (const SourceArtifactConstPtr &sourceArtifact, product->allEnabledFiles()) {
@@ -315,6 +354,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc
continue; // ignore duplicate artifacts
Artifact *artifact = createArtifact(product, sourceArtifact, m_logger);
+ product->buildData->registerAddedArtifact(artifact);
foreach (const FileTag &fileTag, artifact->fileTags)
artifactsPerFileTag[fileTag].insert(artifact);
}
@@ -323,7 +363,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc
typedef QPair<ResolvedTransformerConstPtr, TransformerConstPtr> TrafoPair;
QList<TrafoPair> trafos;
foreach (const ResolvedTransformerConstPtr &rtrafo, product->transformers) {
- ArtifactList inputArtifacts;
+ ArtifactSet inputArtifacts;
foreach (const QString &inputFileName, rtrafo->inputs) {
Artifact *artifact = lookupArtifact(product, inputFileName);
if (Q_UNLIKELY(!artifact))
@@ -348,6 +388,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc
safeConnect(outputArtifact, inputArtifact, m_logger);
foreach (const FileTag &fileTag, outputArtifact->fileTags)
artifactsPerFileTag[fileTag].insert(outputArtifact);
+ product->buildData->registerAddedArtifact(outputArtifact);
RuleArtifactPtr ruleArtifact = RuleArtifact::create();
ruleArtifact->fileName = outputArtifact->filePath();
@@ -382,8 +423,26 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc
}
}
- RulesApplicator(product, artifactsPerFileTag, m_logger).applyAllRules();
- addTargetArtifacts(product, artifactsPerFileTag, m_logger);
+ RuleGraph ruleGraph;
+ ruleGraph.build(product->rules, product->fileTags);
+ CreateRuleNodes crn(product, m_logger);
+ ruleGraph.accept(&crn);
+
+// // ### just a test
+// PhonyTarget *target = new PhonyTarget();
+// target->product = product;
+// TransformerPtr t = Transformer::create();
+// target->setTransformer(t);
+// foreach (BuildGraphNode *node, product->buildData->nodes)
+// target->connectChild(node);
+// product->buildData->nodes.clear();
+// product->buildData->nodes += target;
+// // ### END TEST
+
+// RulesApplicator(product, artifactsPerFileTag, m_logger).applyAllRules();
+// addTargetArtifacts(product, artifactsPerFileTag, m_logger);
+// foreach (Artifact *artifact, product->buildData->targetArtifacts)
+// product->buildData->roots += artifact;
}
RulesEvaluationContextPtr BuildDataResolver::evalContext() const
diff --git a/src/lib/buildgraph/projectbuilddata.h b/src/lib/buildgraph/projectbuilddata.h
index 2268d67f0..5e48fe2ea 100644
--- a/src/lib/buildgraph/projectbuilddata.h
+++ b/src/lib/buildgraph/projectbuilddata.h
@@ -29,7 +29,6 @@
#ifndef QBS_PROJECTBUILDDATA_H
#define QBS_PROJECTBUILDDATA_H
-#include "artifactlist.h"
#include "forward_decls.h"
#include <language/forward_decls.h>
#include <logging/logger.h>
@@ -43,6 +42,8 @@
namespace qbs {
namespace Internal {
+class ArtifactSet;
+class BuildGraphNode;
class FileDependency;
class FileResourceBase;
class ScriptEngine;
@@ -63,12 +64,16 @@ public:
QList<FileResourceBase *> lookupFiles(const Artifact *artifact) const;
void insertFileDependency(FileDependency *dependency);
void updateNodesThatMustGetNewTransformer(const Logger &logger);
+ void updateNodeThatMustGetNewTransformer(Artifact *artifact, const Logger &logger);
void removeArtifactAndExclusiveDependents(Artifact *artifact, const Logger &logger,
- bool removeFromProduct = true, ArtifactList *removedArtifacts = 0);
+ bool removeFromProduct = true, ArtifactSet *removedArtifacts = 0);
void removeArtifact(Artifact *artifact, const Logger &logger, bool removeFromDisk = true,
bool removeFromProduct = true);
+
QSet<FileDependency *> fileDependencies;
+
+ // do not serialize:
RulesEvaluationContextPtr evaluationContext;
QSet<Artifact *> artifactsThatMustGetNewTransformers;
bool isDirty;
@@ -76,7 +81,6 @@ public:
private:
void load(PersistentPool &pool);
void store(PersistentPool &pool) const;
- void updateNodeThatMustGetNewTransformer(Artifact *artifact, const Logger &logger);
typedef QHash<QString, QList<FileResourceBase *> > ResultsPerDirectory;
typedef QHash<QString, ResultsPerDirectory> ArtifactLookupTable;
diff --git a/src/lib/buildgraph/rulegraph.cpp b/src/lib/buildgraph/rulegraph.cpp
index b33c8891d..9a598032b 100644
--- a/src/lib/buildgraph/rulegraph.cpp
+++ b/src/lib/buildgraph/rulegraph.cpp
@@ -100,6 +100,13 @@ QList<RuleConstPtr> RuleGraph::topSorted()
return result;
}
+void RuleGraph::accept(RuleGraphVisitor *visitor) const
+{
+ const RuleConstPtr nullParent;
+ foreach (int rootIndex, m_rootRules)
+ traverse(visitor, nullParent, m_artifacts.at(rootIndex));
+}
+
void RuleGraph::dump() const
{
QByteArray indent;
@@ -210,5 +217,13 @@ QList<RuleConstPtr> RuleGraph::topSort(const RuleConstPtr &rule, QSet<const Rule
return result;
}
+void RuleGraph::traverse(RuleGraphVisitor *visitor, const RuleConstPtr &parentRule,
+ const RuleConstPtr &rule) const
+{
+ visitor->visit(parentRule, rule);
+ foreach (int childIndex, m_children.at(rule->ruleGraphId))
+ traverse(visitor, rule, m_artifacts.at(childIndex));
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/buildgraph/rulegraph.h b/src/lib/buildgraph/rulegraph.h
index 510a4789c..39c2eeaba 100644
--- a/src/lib/buildgraph/rulegraph.h
+++ b/src/lib/buildgraph/rulegraph.h
@@ -42,6 +42,12 @@
namespace qbs {
namespace Internal {
+class RuleGraphVisitor
+{
+public:
+ virtual void visit(const RuleConstPtr &parentRule, const RuleConstPtr &rule) = 0;
+};
+
class RuleGraph
{
public:
@@ -49,6 +55,7 @@ public:
void build(const QSet<RulePtr> &rules, const FileTags &productFileTag);
QList<RuleConstPtr> topSorted();
+ void accept(RuleGraphVisitor *visitor) const;
void dump() const;
@@ -61,6 +68,8 @@ private:
void removeSiblings(const Rule *rule);
QList<RuleConstPtr> topSort(const RuleConstPtr &rule, QSet<const Rule *> *seenRules,
QList<const Rule *> *rulePath);
+ void traverse(RuleGraphVisitor *visitor, const RuleConstPtr &parentRule,
+ const RuleConstPtr &rule) const;
private:
QMap<FileTag, QList<const Rule*> > m_outputFileTagToRule;
diff --git a/src/lib/buildgraph/rulenode.cpp b/src/lib/buildgraph/rulenode.cpp
new file mode 100644
index 000000000..ec3a8c4a5
--- /dev/null
+++ b/src/lib/buildgraph/rulenode.cpp
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "rulenode.h"
+
+#include "artifact.h"
+#include "buildgraph.h"
+#include "buildgraphvisitor.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
+#include "rulesapplicator.h"
+#include "transformer.h"
+#include <language/language.h>
+#include <logging/logger.h>
+
+namespace qbs {
+namespace Internal {
+
+RuleNode::RuleNode()
+{
+}
+
+RuleNode::~RuleNode()
+{
+}
+
+void RuleNode::accept(BuildGraphVisitor *visitor)
+{
+ if (visitor->visit(this))
+ acceptChildren(visitor);
+}
+
+QString RuleNode::toString() const
+{
+ return QLatin1String("RULE ") + m_rule->toString();
+}
+
+/**
+ * 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 RuleNode::removeArtifactAndExclusiveDependents(Artifact *artifact,
+ ArtifactSet *removedArtifacts, const Logger &logger)
+{
+ qDebug() << "REMOVE ARTIFACT" << artifact->toString();
+ if (removedArtifacts)
+ removedArtifacts->insert(artifact);
+ TopLevelProject * const project = artifact->product->topLevelProject();
+ foreach (BuildGraphNode *parentNode, artifact->parents) {
+ Artifact *parent = dynamic_cast<Artifact *>(parentNode);
+ if (!parent)
+ continue;
+ bool removeParent = false;
+ disconnect(parent, artifact, logger);
+ if (parent->children.isEmpty()) {
+ removeParent = true;
+ } else if (parent->transformer) {
+ project->buildData->artifactsThatMustGetNewTransformers += parent;
+ parent->transformer->inputs.remove(artifact);
+ removeParent = parent->transformer->inputs.isEmpty();
+ }
+ if (removeParent)
+ removeArtifactAndExclusiveDependents(parent, removedArtifacts, logger);
+ }
+ project->buildData->removeArtifact(artifact, logger);
+}
+
+void RuleNode::apply(const Logger &logger, ApplicationResult *result)
+{
+ bool hasAddedTags = false;
+ bool hasRemovedTags = false;
+ result->upToDate = false;
+
+ // ### call this only for this rule
+ product->topLevelProject()->buildData->updateNodesThatMustGetNewTransformer(logger);
+
+ foreach (const FileTag &tag, m_rule->inputs) {
+ if (product->buildData->addedArtifactsByFileTag(tag).count())
+ hasAddedTags = true;
+ if (product->buildData->removedArtifactsByFileTag(tag).count())
+ hasRemovedTags = true;
+ if (hasAddedTags && hasRemovedTags)
+ break;
+ }
+ if (!hasAddedTags && !hasRemovedTags) {
+ result->upToDate = true;
+ return;
+ }
+ if (hasRemovedTags) {
+ qDebug() << "@@hasRemovedTags" << m_rule->toString();
+ ArtifactSet artifactsToRemove;
+ foreach (const FileTag &tag, m_rule->inputs) {
+ qDebug() << "@@@@tag" << tag.toString();
+ foreach (Artifact *artifact, product->buildData->removedArtifactsByFileTag(tag)) {
+ qDebug() << "@@@@@@artifact" << artifact->toString();
+ foreach (Artifact *parent, ArtifactSet::fromNodeSet(artifact->parents)) {
+ qDebug() << "@@@@@@@@parent" << parent->toString();
+ if (!parent->transformer || parent->transformer->rule != m_rule
+ || !parent->transformer->inputs.contains(artifact)) {
+ // parent was not created by our rule.
+ continue;
+ }
+ removeArtifactAndExclusiveDependents(parent, &artifactsToRemove, logger);
+ }
+ }
+ }
+ // parents of removed artifacts must update their transformers
+ foreach (Artifact *removedArtifact, artifactsToRemove) {
+ foreach (BuildGraphNode *parentNode, removedArtifact->parents) {
+ Artifact *parent = dynamic_cast<Artifact *>(parentNode);
+ if (!parent)
+ continue;
+ product->topLevelProject()->buildData->artifactsThatMustGetNewTransformers += parent;
+ }
+ }
+ qDeleteAll(artifactsToRemove);
+ }
+ if (hasAddedTags) {
+ // ### daemlicher Umweg ueber artifactsPerFileTag
+ ArtifactsPerFileTagMap artifactsPerFileTag = product->buildData->addedArtifacts();
+ RulesApplicator applicator(product, artifactsPerFileTag, logger);
+ result->createdNodes = applicator.applyRuleInEvaluationContext(m_rule);
+ product->buildData->setAddedArtifacts(artifactsPerFileTag);
+ }
+}
+
+void RuleNode::load(PersistentPool &pool)
+{
+ BuildGraphNode::load(pool);
+ m_rule = pool.idLoadS<Rule>();
+}
+
+void RuleNode::store(PersistentPool &pool) const
+{
+ BuildGraphNode::store(pool);
+ pool.store(m_rule);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/buildgraph/rulenode.h b/src/lib/buildgraph/rulenode.h
new file mode 100644
index 000000000..050aeda6a
--- /dev/null
+++ b/src/lib/buildgraph/rulenode.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_RULENODE_H
+#define QBS_RULENODE_H
+
+#include "artifactset.h"
+#include "buildgraphnode.h"
+#include <language/forward_decls.h>
+
+#include <QVector>
+
+namespace qbs {
+namespace Internal {
+
+class Logger;
+
+class RuleNode : public BuildGraphNode
+{
+public:
+ RuleNode();
+ ~RuleNode();
+
+ void setRule(const RuleConstPtr &rule) { m_rule = rule; }
+ const RuleConstPtr &rule() const { return m_rule; }
+
+ void accept(BuildGraphVisitor *visitor);
+ QString toString() const;
+
+ struct ApplicationResult
+ {
+ bool upToDate;
+ QVector<BuildGraphNode *> createdNodes;
+ };
+
+ void apply(const Logger &logger, ApplicationResult *result);
+
+protected:
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+ Type type() const { return RuleNodeType; }
+
+private:
+ void removeArtifactAndExclusiveDependents(Artifact *artifact, ArtifactSet *removedArtifacts,
+ const Logger &logger);
+ void removeArtifact(Artifact *artifact, const Logger &logger);
+
+ RuleConstPtr m_rule;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_RULENODE_H
diff --git a/src/lib/buildgraph/rulesapplicator.cpp b/src/lib/buildgraph/rulesapplicator.cpp
index 0e3e95374..1eadc7773 100644
--- a/src/lib/buildgraph/rulesapplicator.cpp
+++ b/src/lib/buildgraph/rulesapplicator.cpp
@@ -64,6 +64,14 @@ void RulesApplicator::applyAllRules()
applyRule(rule);
}
+QVector<BuildGraphNode *> RulesApplicator::applyRuleInEvaluationContext(const RuleConstPtr &rule)
+{
+ m_createdArtifacts.clear();
+ RulesEvaluationContext::Scope s(m_product->topLevelProject()->buildData->evaluationContext.data());
+ applyRule(rule);
+ return m_createdArtifacts;
+}
+
void RulesApplicator::applyRule(const RuleConstPtr &rule)
{
m_rule = rule;
@@ -72,14 +80,14 @@ void RulesApplicator::applyRule(const RuleConstPtr &rule)
setupScriptEngineForProduct(engine(), m_product, m_rule, prepareScriptContext, this);
m_productObjectId = prepareScriptContext.property(QLatin1String("product")).objectId();
- ArtifactList inputArtifacts;
+ ArtifactSet inputArtifacts;
foreach (const FileTag &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, prepareScriptContext);
} else { // apply the rule once for each input
- ArtifactList lst;
+ ArtifactSet lst;
foreach (Artifact * const inputArtifact, inputArtifacts) {
setupScriptEngineForArtifact(inputArtifact);
lst += inputArtifact;
@@ -94,8 +102,18 @@ static void copyProperty(const QString &name, const QScriptValue &src, QScriptVa
dst.setProperty(name, src.property(name));
}
-void RulesApplicator::doApply(const ArtifactList &inputArtifacts,
- QScriptValue &prepareScriptContext)
+static QStringList toStringList(const ArtifactSet &artifacts)
+{
+ QStringList lst;
+ foreach (const Artifact *artifact, artifacts) {
+ const QString str = artifact->filePath() + QLatin1String(" [")
+ + artifact->fileTags.toStringList().join(QLatin1String(", ")) + QLatin1Char(']');
+ lst << str;
+ }
+ return lst;
+}
+
+void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &prepareScriptContext)
{
evalContext()->checkForCancelation();
@@ -107,12 +125,12 @@ void RulesApplicator::doApply(const ArtifactList &inputArtifacts,
QList<QPair<const RuleArtifact *, Artifact *> > ruleArtifactArtifactMap;
QList<Artifact *> outputArtifacts;
- ArtifactList usingArtifacts;
+ ArtifactSet usingArtifacts;
if (!m_rule->usings.isEmpty()) {
const FileTags usingsFileTags = m_rule->usings;
foreach (const ResolvedProductPtr &dep, m_product->dependencies) {
QBS_CHECK(dep->buildData);
- ArtifactList artifactsToCheck;
+ ArtifactSet artifactsToCheck;
foreach (Artifact *targetArtifact, dep->buildData->targetArtifacts)
artifactsToCheck.unite(targetArtifact->transformer->outputs);
foreach (Artifact *artifact, artifactsToCheck) {
@@ -143,7 +161,7 @@ void RulesApplicator::doApply(const ArtifactList &inputArtifacts,
loggedConnect(outputArtifact, dependency, m_logger);
// Transformer setup
- for (ArtifactList::const_iterator it = usingArtifacts.constBegin();
+ for (ArtifactSet::const_iterator it = usingArtifacts.constBegin();
it != usingArtifacts.constEnd(); ++it)
{
Artifact *dep = *it;
@@ -228,7 +246,7 @@ void RulesApplicator::setupScriptEngineForArtifact(Artifact *artifact)
}
Artifact *RulesApplicator::createOutputArtifact(const RuleArtifactConstPtr &ruleArtifact,
- const ArtifactList &inputArtifacts)
+ const ArtifactSet &inputArtifacts)
{
QScriptValue scriptValue = engine()->evaluate(ruleArtifact->fileName);
if (Q_UNLIKELY(scriptValue.isError() || engine()->hasUncaughtException()))
@@ -280,6 +298,7 @@ Artifact *RulesApplicator::createOutputArtifact(const RuleArtifactConstPtr &rule
outputArtifact->alwaysUpdated = ruleArtifact->alwaysUpdated;
outputArtifact->properties = m_product->properties;
insertArtifact(m_product, outputArtifact, m_logger);
+ m_createdArtifacts += outputArtifact;
}
if (outputArtifact->fileTags.isEmpty())
diff --git a/src/lib/buildgraph/rulesapplicator.h b/src/lib/buildgraph/rulesapplicator.h
index 622391c5b..6a2e4436a 100644
--- a/src/lib/buildgraph/rulesapplicator.h
+++ b/src/lib/buildgraph/rulesapplicator.h
@@ -29,22 +29,24 @@
#ifndef QBS_RULESAPPLICATOR_H
#define QBS_RULESAPPLICATOR_H
-#include "artifactlist.h"
+#include "artifactset.h"
#include "forward_decls.h"
#include <language/filetags.h>
#include <language/forward_decls.h>
#include <language/scriptpropertyobserver.h>
#include <logging/logger.h>
-#include <QMap>
+#include <QHash>
#include <QScriptValue>
#include <QString>
+#include <QVector>
namespace qbs {
namespace Internal {
+class BuildGraphNode;
class ScriptEngine;
-typedef QMap<FileTag, ArtifactList> ArtifactsPerFileTagMap;
+typedef QHash<FileTag, ArtifactSet> ArtifactsPerFileTagMap;
class RulesApplicator : private ScriptPropertyObserver
{
@@ -52,13 +54,14 @@ public:
RulesApplicator(const ResolvedProductPtr &product, ArtifactsPerFileTagMap &artifactsPerFileTag,
const Logger &logger);
void applyAllRules();
+ QVector<BuildGraphNode *> applyRuleInEvaluationContext(const RuleConstPtr &rule);
void applyRule(const RuleConstPtr &rule);
private:
- void doApply(const ArtifactList &inputArtifacts, QScriptValue &prepareScriptContext);
+ void doApply(const ArtifactSet &inputArtifacts, QScriptValue &prepareScriptContext);
void setupScriptEngineForArtifact(Artifact *artifact);
Artifact *createOutputArtifact(const RuleArtifactConstPtr &ruleArtifact,
- const ArtifactList &inputArtifacts);
+ const ArtifactSet &inputArtifacts);
QString resolveOutPath(const QString &path) const;
RulesEvaluationContextPtr evalContext() const;
ScriptEngine *engine() const;
@@ -67,6 +70,7 @@ private:
const ResolvedProductPtr m_product;
ArtifactsPerFileTagMap &m_artifactsPerFileTag;
+ QVector<BuildGraphNode *> m_createdArtifacts;
RuleConstPtr m_rule;
TransformerPtr m_transformer;
diff --git a/src/lib/buildgraph/tagscannerrunner.cpp b/src/lib/buildgraph/tagscannerrunner.cpp
new file mode 100644
index 000000000..c5c26b6d7
--- /dev/null
+++ b/src/lib/buildgraph/tagscannerrunner.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "tagscannerrunner.h"
+
+#include "artifact.h"
+#include "moctagscanner.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
+#include "rulesevaluationcontext.h"
+#include <jsextensions/jsextensions.h>
+#include <language/language.h>
+#include <language/scriptengine.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+
+#include <QDebug>
+
+namespace qbs {
+namespace Internal {
+
+TagScannerRunner::TagScannerRunner(QScriptValue &contextObject, ScanResultCache *src,
+ const ResolvedProductPtr &product, const TagScannerConstPtr &tagScanner,
+ const Logger &logger)
+ : m_scanResultCache(src)
+ , m_product(product)
+ , m_tagScanner(tagScanner)
+ , m_logger(logger)
+ , m_engine(0)
+ , m_contextObject(contextObject)
+ , m_mocTagScanner(0)
+{
+}
+
+TagScannerRunner::~TagScannerRunner()
+{
+ delete m_mocTagScanner;
+}
+
+void TagScannerRunner::apply(Artifact *artifact)
+{
+ qDebug() << "SCAN SOURCE ARTIFACT FOR FILE TAGS" << artifact->filePath();
+ setup();
+
+ QScriptValue v;
+ if (m_mocTagScanner) {
+ v = mocScanResult(artifact);
+ } else {
+ m_scope.setProperty(QLatin1String("artifact"), createArtifactScriptObject(artifact));
+ v = m_scanFunction.call(m_scanFunctionThisObject);
+ }
+ if (v.isError())
+ throw ErrorInfo(v.toString(), m_tagScanner->scanScript()->location);
+ if (!v.isArray())
+ throw ErrorInfo(Tr::tr("TagScanner.scan must return an array of strings."),
+ m_tagScanner->scanScript()->location);
+
+
+ FileTags removedTags = m_tagScanner->outputTags();
+ removedTags.intersect(artifact->fileTags);
+ artifact->fileTags -= m_tagScanner->outputTags();
+ FileTags addedTags;
+ const int c = v.property(QLatin1String("length")).toInt32();
+ for (int i = 0; i < c; ++i) {
+ const FileTag tag(v.property(i).toString().toUtf8());
+ if (!m_tagScanner->outputTags().contains(tag))
+ throw ErrorInfo(Tr::tr("TagScanner.scan returned the file tag '%1' which doesn't appear"
+ " in TagScanner.outputTags.").arg(tag.toString()),
+ m_tagScanner->scanScript()->location);
+ artifact->fileTags += tag;
+ removedTags -= tag;
+ addedTags += tag;
+ }
+ qDebug() << "ADDED TAGS" << addedTags.toStringList() << "REMOVED TAGS" << removedTags.toStringList();
+
+ foreach (const FileTag &t, addedTags) {
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[TAGSCANNER] add tag " << t.toString()
+ << " to " << artifact->filePath();
+ m_product->buildData->registerAddedFileTag(t, artifact);
+ }
+ foreach (const FileTag &t, removedTags) {
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[TAGSCANNER] remove tag " << t.toString()
+ << " from " << artifact->filePath();
+ m_product->buildData->registerRemovedFileTag(t, artifact);
+ }
+}
+
+void TagScannerRunner::setup()
+{
+ if (m_engine)
+ return;
+
+ m_engine = m_product->topLevelProject()->buildData->evaluationContext->engine();
+ QBS_CHECK(m_engine);
+ m_scanFunctionThisObject = m_engine->newObject();
+ if (!m_contextObject.isObject())
+ m_contextObject = m_engine->newObject();
+ m_contextObject.setProperty(m_tagScanner->name(), m_scanFunctionThisObject);
+
+ if (m_tagScanner->name() == QLatin1String("QtMocCppScanner")) {
+ m_mocTagScanner = new MocTagScanner(MocTagScanner::CppFileType, m_contextObject,
+ m_scanResultCache, m_logger);
+ return;
+ } else if (m_tagScanner->name() == QLatin1String("QtMocHppScanner")) {
+ m_mocTagScanner = new MocTagScanner(MocTagScanner::HppFileType, m_contextObject,
+ m_scanResultCache, m_logger);
+ return;
+ }
+
+ m_scope = m_engine->newObject();
+ m_engine->currentContext()->pushScope(m_scope);
+ m_scanFunction = m_engine->evaluate(m_tagScanner->scanScript()->sourceCode,
+ m_tagScanner->scanScript()->location.fileName(),
+ m_tagScanner->scanScript()->location.line());
+ m_engine->currentContext()->popScope();
+ if (m_scanFunction.isError())
+ throw ErrorInfo(m_scanFunction.toString(), m_tagScanner->scanScript()->location);
+ if (m_engine->hasUncaughtException())
+ throw ErrorInfo(Tr::tr("Exception in TagScanner.scan function: %1"),
+ m_engine->uncaughtExceptionBacktrace().join(QLatin1String("\n")));
+ QBS_CHECK(m_scanFunction.isFunction());
+
+ // ### no static list of extensions!
+ QStringList exts = QStringList() << "TextFile" << "Process";
+ JsExtensions::setupExtensions(exts, m_scope);
+}
+
+QScriptValue TagScannerRunner::createArtifactScriptObject(Artifact *artifact)
+{
+ QScriptValue obj = m_engine->newObject();
+ obj.setProperty(QLatin1String("filePath"), artifact->filePath());
+ QScriptValue tags = m_engine->newArray(artifact->fileTags.count());
+ int idx = 0;
+ foreach (const FileTag &tag, artifact->fileTags)
+ tags.setProperty(idx++, tag.toString());
+ obj.setProperty(QLatin1String("fileTags"), tags);
+ return obj;
+}
+
+QScriptValue TagScannerRunner::mocScanResult(Artifact *artifact) const
+{
+ const FileTags tags = m_mocTagScanner->apply(artifact);
+ QScriptValue v = m_engine->newArray(tags.count());
+ int i = 0;
+ foreach (const FileTag &tag, tags)
+ v.setProperty(i++, tag.toString());
+ return v;
+}
+
+static void runTagScannersTopOrdered_impl(QScriptValue &contextObject,
+ ScanResultCache *scanResultCache,
+ const ResolvedProductPtr &product,
+ const TagScannerConstPtr &tagScanner,
+ const QHash<FileTag, QList<Artifact *> > &sourceArtifacts,
+ const Logger &logger)
+{
+ foreach (const TagScannerConstPtr &dependency, tagScanner->dependencies())
+ runTagScannersTopOrdered_impl(contextObject, scanResultCache, product, dependency,
+ sourceArtifacts, logger);
+
+ if (logger.debugEnabled())
+ logger.qbsDebug() << "[EXEC] TagScanner " << tagScanner->name();
+
+ TagScannerRunner tsr(contextObject, scanResultCache, product, tagScanner, logger);
+ foreach (const FileTag &tag, tagScanner->inputTags())
+ foreach (Artifact *sourceArtifact, sourceArtifacts.value(tag))
+ tsr.apply(sourceArtifact);
+}
+
+void runTagScannersTopOrdered(ScanResultCache *scanResultCache, const ResolvedProductPtr &product,
+ const TagScannerConstPtr &tagScanner,
+ const QHash<FileTag, QList<Artifact *> > &sourceArtifacts, const Logger &logger)
+{
+ QScriptValue contextObject;
+ runTagScannersTopOrdered_impl(contextObject, scanResultCache, product, tagScanner,
+ sourceArtifacts, logger);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/buildgraph/tagscannerrunner.h b/src/lib/buildgraph/tagscannerrunner.h
new file mode 100644
index 000000000..c6a7069ab
--- /dev/null
+++ b/src/lib/buildgraph/tagscannerrunner.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Build Suite.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_TAGSCANNERRUNNER_H
+#define QBS_TAGSCANNERRUNNER_H
+
+#include <language/forward_decls.h>
+#include <QScriptValue>
+
+namespace qbs {
+namespace Internal {
+
+class Artifact;
+class FileTag;
+class Logger;
+class MocTagScanner;
+class ScanResultCache;
+class ScriptEngine;
+
+class TagScannerRunner
+{
+public:
+ TagScannerRunner(QScriptValue &contextObject, ScanResultCache *src,
+ const ResolvedProductPtr &product, const TagScannerConstPtr &tagScanner,
+ const Logger &logger);
+ ~TagScannerRunner();
+
+ void apply(Artifact *artifact);
+
+private:
+ void setup();
+ QScriptValue createArtifactScriptObject(Artifact *artifact);
+ QScriptValue mocScanResult(Artifact *artifact) const;
+
+ ScanResultCache *m_scanResultCache;
+ const ResolvedProductPtr m_product;
+ const TagScannerConstPtr m_tagScanner;
+ const Logger &m_logger;
+ ScriptEngine *m_engine;
+ QScriptValue m_scope;
+ QScriptValue &m_contextObject;
+ QScriptValue m_scanFunctionThisObject;
+ QScriptValue m_scanFunction;
+ MocTagScanner *m_mocTagScanner;
+};
+
+void runTagScannersTopOrdered(ScanResultCache *scanResultCache,
+ const ResolvedProductPtr &product,
+ const TagScannerConstPtr &tagScanner,
+ const QHash<FileTag, QList<Artifact *> > &sourceArtifacts,
+ const Logger &logger);
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_TAGSCANNERRUNNER_H
diff --git a/src/lib/buildgraph/transformer.cpp b/src/lib/buildgraph/transformer.cpp
index 13d98c284..4660fdafe 100644
--- a/src/lib/buildgraph/transformer.cpp
+++ b/src/lib/buildgraph/transformer.cpp
@@ -63,13 +63,14 @@ QScriptValue Transformer::translateFileConfig(QScriptEngine *scriptEngine, Artif
return artifactConfig;
}
-QScriptValue Transformer::translateInOutputs(QScriptEngine *scriptEngine, const ArtifactList &artifacts, const QString &defaultModuleName)
+QScriptValue Transformer::translateInOutputs(QScriptEngine *scriptEngine, const ArtifactSet &artifacts, const QString &defaultModuleName)
{
typedef QMap<QString, QList<Artifact*> > TagArtifactsMap;
TagArtifactsMap tagArtifactsMap;
- foreach (Artifact *artifact, artifacts)
+ foreach (Artifact *artifact, artifacts) {
foreach (const FileTag &fileTag, artifact->fileTags)
tagArtifactsMap[fileTag.toString()].append(artifact);
+ }
QScriptValue jsTagFiles = scriptEngine->newObject();
for (TagArtifactsMap::const_iterator tag = tagArtifactsMap.constBegin(); tag != tagArtifactsMap.constEnd(); ++tag) {
diff --git a/src/lib/buildgraph/transformer.h b/src/lib/buildgraph/transformer.h
index e23cee65e..b79e4030c 100644
--- a/src/lib/buildgraph/transformer.h
+++ b/src/lib/buildgraph/transformer.h
@@ -30,7 +30,7 @@
#ifndef QBS_TRANSFORMER_H
#define QBS_TRANSFORMER_H
-#include "artifactlist.h"
+#include "artifactset.h"
#include "forward_decls.h"
#include <language/forward_decls.h>
#include <language/property.h>
@@ -53,8 +53,8 @@ public:
~Transformer();
- ArtifactList inputs; // Subset of "children of all outputs".
- ArtifactList outputs;
+ ArtifactSet inputs; // Subset of "children of all outputs".
+ ArtifactSet outputs;
RuleConstPtr rule;
QList<AbstractCommand *> commands;
PropertyList propertiesRequestedFromProductInPrepareScript;
@@ -64,7 +64,7 @@ public:
Artifact *artifact,
const QString &defaultModuleName);
static QScriptValue translateInOutputs(QScriptEngine *scriptEngine,
- const ArtifactList &artifacts,
+ const ArtifactSet &artifacts,
const QString &defaultModuleName);
ResolvedProductPtr product() const;
diff --git a/src/lib/language/builtindeclarations.cpp b/src/lib/language/builtindeclarations.cpp
index ebc876b60..7eb961d41 100644
--- a/src/lib/language/builtindeclarations.cpp
+++ b/src/lib/language/builtindeclarations.cpp
@@ -50,6 +50,7 @@ BuiltinDeclarations::BuiltinDeclarations()
addPropertyOptionsItem();
addRuleItem();
addSubprojectItem();
+ addTagScannerItem();
addTransformerItem();
}
@@ -341,6 +342,22 @@ void BuiltinDeclarations::addSubprojectItem()
m_builtins[QLatin1String("SubProject")] = properties;
}
+void BuiltinDeclarations::addTagScannerItem()
+{
+ QList<PropertyDeclaration> properties;
+ properties += PropertyDeclaration(QLatin1String("name"),
+ PropertyDeclaration::String);
+ properties += PropertyDeclaration(QLatin1String("dependencies"),
+ PropertyDeclaration::StringList);
+ properties += PropertyDeclaration(QLatin1String("inputTags"),
+ PropertyDeclaration::StringList);
+ properties += PropertyDeclaration(QLatin1String("outputTags"),
+ PropertyDeclaration::StringList);
+ properties += PropertyDeclaration(QLatin1String("scan"),
+ PropertyDeclaration::Verbatim);
+ m_builtins[QLatin1String("TagScanner")] = properties;
+}
+
void BuiltinDeclarations::addTransformerItem()
{
QList<PropertyDeclaration> properties;
diff --git a/src/lib/language/builtindeclarations.h b/src/lib/language/builtindeclarations.h
index 4cbd352a7..b10c3eae9 100644
--- a/src/lib/language/builtindeclarations.h
+++ b/src/lib/language/builtindeclarations.h
@@ -64,6 +64,7 @@ private:
void addPropertyOptionsItem();
void addRuleItem();
void addSubprojectItem();
+ void addTagScannerItem();
void addTransformerItem();
QString m_languageVersion;
diff --git a/src/lib/language/forward_decls.h b/src/lib/language/forward_decls.h
index 5087258fc..d8d0d3d4f 100644
--- a/src/lib/language/forward_decls.h
+++ b/src/lib/language/forward_decls.h
@@ -66,6 +66,10 @@ class FileTagger;
typedef QSharedPointer<FileTagger> FileTaggerPtr;
typedef QSharedPointer<const FileTagger> FileTaggerConstPtr;
+class TagScanner;
+typedef QSharedPointer<TagScanner> TagScannerPtr;
+typedef QSharedPointer<const TagScanner> TagScannerConstPtr;
+
class ResolvedProduct;
typedef QSharedPointer<ResolvedProduct> ResolvedProductPtr;
typedef QSharedPointer<const ResolvedProduct> ResolvedProductConstPtr;
diff --git a/src/lib/language/language.cpp b/src/lib/language/language.cpp
index 0530a78a6..f856a052b 100644
--- a/src/lib/language/language.cpp
+++ b/src/lib/language/language.cpp
@@ -104,6 +104,32 @@ void FileTagger::store(PersistentPool &pool) const
}
/*!
+ * \class TagScanner
+ * \brief The \c TagScanner class represents an entity that scans and tags source artifacts.
+ */
+void TagScanner::load(PersistentPool &pool)
+{
+ m_name = pool.idLoadString();
+ pool.stream()
+ >> m_location
+ >> m_inputTags
+ >> m_outputTags;
+ m_scanScript = pool.idLoadS<ScriptFunction>();
+ pool.loadContainerS(m_dependencies);
+}
+
+void TagScanner::store(PersistentPool &pool) const
+{
+ pool.storeString(m_name);
+ pool.stream()
+ << m_location
+ << m_inputTags
+ << m_outputTags;
+ pool.store(m_scanScript);
+ pool.storeContainer(m_dependencies);
+}
+
+/*!
* \class SourceArtifact
* \brief The \c SourceArtifact class represents a source file.
* Everything except the file path is inherited from the surrounding \c ResolvedGroup.
@@ -352,8 +378,9 @@ static bool modulesAreEqual(const ResolvedModuleConstPtr &m1, const ResolvedModu
QString Rule::toString() const
{
- return QLatin1Char('[') + inputs.toStringList().join(QLatin1String(",")) + QLatin1String(" -> ")
- + outputFileTags().toStringList().join(QLatin1String(",")) + QLatin1Char(']');
+ return QLatin1Char('[') + outputFileTags().toStringList().join(QLatin1String(","))
+ + QLatin1String("][")
+ + inputs.toStringList().join(QLatin1String(",")) + QLatin1Char(']');
}
FileTags Rule::outputFileTags() const
@@ -401,6 +428,14 @@ ResolvedProduct::~ResolvedProduct()
{
}
+void ResolvedProduct::accept(BuildGraphVisitor *visitor) const
+{
+ if (!buildData)
+ return;
+ foreach (Artifact *artifact, buildData->targetArtifacts)
+ artifact->accept(visitor);
+}
+
/*!
* \brief Returns all files of all groups as source artifacts.
* This includes the expanded list of wildcards.
@@ -455,6 +490,7 @@ void ResolvedProduct::load(PersistentPool &pool)
pool.loadContainerS(rules);
pool.loadContainerS(dependencies);
pool.loadContainerS(fileTaggers);
+ pool.loadContainerS(tagScanners);
pool.loadContainerS(modules);
pool.loadContainerS(transformers);
pool.loadContainerS(groups);
@@ -478,6 +514,7 @@ void ResolvedProduct::store(PersistentPool &pool) const
pool.storeContainer(rules);
pool.storeContainer(dependencies);
pool.storeContainer(fileTaggers);
+ pool.storeContainer(tagScanners);
pool.storeContainer(modules);
pool.storeContainer(transformers);
pool.storeContainer(groups);
@@ -678,13 +715,13 @@ TopLevelProject *ResolvedProduct::topLevelProject() const
static QStringList findGeneratedFiles(const Artifact *base, const FileTags &tags)
{
QStringList result;
- foreach (const Artifact *parent, base->parents) {
+ foreach (const Artifact *parent, base->parentArtifacts()) {
if (tags.isEmpty() || parent->fileTags.matches(tags))
result << parent->filePath();
}
if (result.isEmpty() || tags.isEmpty())
- foreach (const Artifact *parent, base->parents)
+ foreach (const Artifact *parent, base->parentArtifacts())
result << findGeneratedFiles(parent, tags);
return result;
@@ -696,7 +733,7 @@ QStringList ResolvedProduct::generatedFiles(const QString &baseFile, const FileT
if (!data)
return QStringList();
- foreach (const Artifact *art, data->artifacts) {
+ foreach (const Artifact *art, ArtifactSet::fromNodeSet(data->nodes)) {
if (art->filePath() == baseFile)
return findGeneratedFiles(art, tags);
}
@@ -707,6 +744,12 @@ ResolvedProject::ResolvedProject() : enabled(true), m_topLevelProject(0)
{
}
+void ResolvedProject::accept(BuildGraphVisitor *visitor) const
+{
+ foreach (const ResolvedProductPtr &product, products)
+ product->accept(visitor);
+}
+
TopLevelProject *ResolvedProject::topLevelProject()
{
if (m_topLevelProject)
@@ -755,8 +798,13 @@ void ResolvedProject::load(PersistentPool &pool)
for (; --count >= 0;) {
ResolvedProductPtr rProduct = pool.idLoadS<ResolvedProduct>();
if (rProduct->buildData) {
- foreach (Artifact * const a, rProduct->buildData->artifacts)
- a->product = rProduct;
+ foreach (BuildGraphNode * const node, rProduct->buildData->nodes) {
+ node->product = rProduct;
+
+ // restore parent links
+ foreach (BuildGraphNode *child, node->children)
+ child->parents.insert(node);
+ }
}
products.append(rProduct);
}
@@ -810,6 +858,13 @@ QString TopLevelProject::deriveBuildDirectory(const QString &buildRoot, const QS
return buildRoot + QLatin1Char('/') + id;
}
+void TopLevelProject::accept(BuildGraphVisitor *visitor) const
+{
+ ResolvedProject::accept(visitor);
+ foreach (const ResolvedProjectPtr &subProject, subProjects)
+ subProject->accept(visitor);
+}
+
void TopLevelProject::setBuildConfiguration(const QVariantMap &config)
{
m_buildConfiguration = config;
@@ -1040,7 +1095,7 @@ bool operator==(const SourceArtifact &sa1, const SourceArtifact &sa2)
&& sa1.properties->value() == sa2.properties->value();
}
-bool sourceArtifactListsAreEqual(const QList<SourceArtifactPtr> &l1,
+bool sourceArtifactSetsAreEqual(const QList<SourceArtifactPtr> &l1,
const QList<SourceArtifactPtr> &l2)
{
return listsAreEqual(l1, l2);
@@ -1050,7 +1105,7 @@ bool operator==(const ResolvedTransformer &t1, const ResolvedTransformer &t2)
{
return modulesAreEqual(t1.module, t2.module)
&& t1.inputs.toSet() == t2.inputs.toSet()
- && sourceArtifactListsAreEqual(t1.outputs, t2.outputs)
+ && sourceArtifactSetsAreEqual(t1.outputs, t2.outputs)
&& *t1.transform == *t2.transform;
}
diff --git a/src/lib/language/language.h b/src/lib/language/language.h
index 183a79e44..0af4cb621 100644
--- a/src/lib/language/language.h
+++ b/src/lib/language/language.h
@@ -61,6 +61,7 @@ QT_END_NAMESPACE
namespace qbs {
namespace Internal {
class BuildGraphLoader;
+class BuildGraphVisitor;
class FileTagger : public PersistentObject
{
@@ -86,6 +87,45 @@ private:
FileTags m_fileTags;
};
+class TagScanner : public PersistentObject
+{
+public:
+ static TagScannerPtr create() { return TagScannerPtr(new TagScanner); }
+
+ void init(const CodeLocation &location, const QString &name, const FileTags &inTags,
+ const FileTags &outTags, const ScriptFunctionConstPtr &scanScript)
+ {
+ m_location = location;
+ m_name = name;
+ m_inputTags = inTags;
+ m_outputTags = outTags;
+ m_scanScript = scanScript;
+ }
+
+ const CodeLocation &location() const { return m_location; }
+ const QString &name() const { return m_name; }
+ const FileTags &inputTags() const { return m_inputTags; }
+ const FileTags &outputTags() const { return m_outputTags; }
+ const ScriptFunctionConstPtr &scanScript() const { return m_scanScript; }
+
+ void setDependencies(const QList<TagScannerConstPtr> &deps) { m_dependencies = deps; }
+ const QList<TagScannerConstPtr> &dependencies() const { return m_dependencies; }
+
+private:
+ TagScanner()
+ {}
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+
+ CodeLocation m_location;
+ QString m_name;
+ FileTags m_inputTags;
+ FileTags m_outputTags;
+ ScriptFunctionConstPtr m_scanScript;
+ QList<TagScannerConstPtr> m_dependencies;
+};
+
class RuleArtifact : public PersistentObject
{
public:
@@ -142,7 +182,7 @@ inline bool operator!=(const SourceArtifact &sa1, const SourceArtifact &sa2) {
return !(sa1 == sa2);
}
-bool sourceArtifactListsAreEqual(const QList<SourceArtifactPtr> &l1,
+bool sourceArtifactSetsAreEqual(const QList<SourceArtifactPtr> &l1,
const QList<SourceArtifactPtr> &l2);
class SourceWildCards : public PersistentObject
@@ -354,6 +394,7 @@ public:
QSet<RulePtr> rules;
QSet<ResolvedProductPtr> dependencies;
QList<FileTaggerConstPtr> fileTaggers;
+ QList<TagScannerConstPtr> tagScanners;
QList<ResolvedModuleConstPtr> modules;
QList<ResolvedTransformerConstPtr> transformers;
QList<GroupPtr> groups;
@@ -364,6 +405,7 @@ public:
mutable QProcessEnvironment runEnvironment; // must not be saved
QHash<QString, QString> executablePathCache;
+ void accept(BuildGraphVisitor *visitor) const;
QList<SourceArtifactPtr> allFiles() const;
QList<SourceArtifactPtr> allEnabledFiles() const;
FileTags fileTagsForFileName(const QString &fileName) const;
@@ -394,6 +436,8 @@ public:
QList<ResolvedProjectPtr> subProjects;
WeakPointer<ResolvedProject> parentProject;
+ virtual void accept(BuildGraphVisitor *visitor) const;
+
void setProjectProperties(const QVariantMap &config) { m_projectProperties = config; }
const QVariantMap &projectProperties() const { return m_projectProperties; }
@@ -432,6 +476,7 @@ public:
QSet<QString> buildSystemFiles;
+ void accept(BuildGraphVisitor *visitor) const;
void setBuildConfiguration(const QVariantMap &config);
const QVariantMap &buildConfiguration() const { return m_buildConfiguration; }
QString id() const { return m_id; }
diff --git a/src/lib/language/projectresolver.cpp b/src/lib/language/projectresolver.cpp
index 7de84608c..5763d7c5e 100644
--- a/src/lib/language/projectresolver.cpp
+++ b/src/lib/language/projectresolver.cpp
@@ -343,6 +343,8 @@ void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext)
foreach (const Item::Module &module, item->modules())
resolveModule(module.name, module.item, projectContext);
+ resolveTagScannerDependencies();
+
m_productContext = 0;
if (m_progressObserver)
m_progressObserver->incrementProgressValue();
@@ -376,6 +378,7 @@ void ProjectResolver::resolveModule(const QStringList &moduleName, Item *item,
mapping["PropertyOptions"] = &ProjectResolver::ignoreItem;
mapping["Depends"] = &ProjectResolver::ignoreItem;
mapping["Probe"] = &ProjectResolver::ignoreItem;
+ mapping["TagScanner"] = &ProjectResolver::resolveTagScanner;
foreach (Item *child, item->children())
callItemFunction(mapping, child, projectContext);
@@ -685,6 +688,57 @@ void ProjectResolver::resolveFileTagger(Item *item, ProjectContext *projectConte
fileTaggers += FileTagger::create(patterns, fileTags);
}
+void ProjectResolver::resolveTagScanner(Item *item, ProjectResolver::ProjectContext *projectContext)
+{
+ Q_UNUSED(projectContext);
+ checkCancelation();
+ const QString name = m_evaluator->stringValue(item, QLatin1String("name"));
+ if (name.isEmpty())
+ throw ErrorInfo(Tr::tr("TagScanner items must have a name."), item->location());
+ ProductContext::TagScannerHash::mapped_type &cached = m_productContext->tagScanners[name];
+ TagScannerPtr &tagScanner = cached.first;
+ if (tagScanner) {
+ throw ErrorInfo(Tr::tr("TagScanner '%1' conflicts with TagScanner at %2.").arg(name,
+ tagScanner->location().toString()), item->location());
+ }
+
+ cached.second = m_evaluator->stringListValue(item, QLatin1String("dependencies"));
+ tagScanner = TagScanner::create();
+ tagScanner->init(
+ item->location(),
+ name,
+ m_evaluator->fileTagsValue(item, QLatin1String("inputTags")),
+ m_evaluator->fileTagsValue(item, QLatin1String("outputTags")),
+ scriptFunctionValue(item, QLatin1String("scan")));
+}
+
+void ProjectResolver::resolveTagScannerDependencies()
+{
+ QSet<TagScannerConstPtr> rootTagScanners;
+ ProductContext::TagScannerHash::const_iterator it = m_productContext->tagScanners.constBegin();
+ for (; it != m_productContext->tagScanners.constEnd(); ++it)
+ rootTagScanners += it.value().first;
+
+ it = m_productContext->tagScanners.constBegin();
+ for (; it != m_productContext->tagScanners.constEnd(); ++it) {
+ const TagScannerPtr &tagScanner = it.value().first;
+ QList<TagScannerConstPtr> dependencies;
+ foreach (const QString &dependencyName, it.value().second) {
+ const TagScannerPtr &dependency
+ = m_productContext->tagScanners.value(dependencyName).first;
+ if (!dependency)
+ throw ErrorInfo(
+ Tr::tr("Unsatisfied TagScanner dependency '%1'.").arg(dependencyName),
+ tagScanner->location());
+ dependencies += dependency;
+ rootTagScanners.remove(dependency);
+ }
+ tagScanner->setDependencies(dependencies.toSet().toList());
+ }
+
+ m_productContext->product->tagScanners = rootTagScanners.toList();
+}
+
void ProjectResolver::resolveTransformer(Item *item, ProjectContext *projectContext)
{
checkCancelation();
diff --git a/src/lib/language/projectresolver.h b/src/lib/language/projectresolver.h
index b23f19341..10d5e7fd6 100644
--- a/src/lib/language/projectresolver.h
+++ b/src/lib/language/projectresolver.h
@@ -78,6 +78,8 @@ private:
{
ResolvedProductPtr product;
Item *item;
+ typedef QHash<QString, QPair<TagScannerPtr, QStringList> > TagScannerHash;
+ TagScannerHash tagScanners;
};
struct ModuleContext
@@ -103,6 +105,8 @@ private:
const QStringList &namePrefix,
StringListSet *seenBindings);
void resolveFileTagger(Item *item, ProjectContext *projectContext);
+ void resolveTagScanner(Item *item, ProjectContext *projectContext);
+ void resolveTagScannerDependencies();
void resolveTransformer(Item *item, ProjectContext *projectContext);
void resolveExport(Item *item, ProjectContext *projectContext);
void resolveProductDependencies(ProjectContext *projectContext);
diff --git a/src/lib/lib.qbs b/src/lib/lib.qbs
index f5d08380d..505050978 100644
--- a/src/lib/lib.qbs
+++ b/src/lib/lib.qbs
@@ -79,16 +79,17 @@ Product {
"artifact.h",
"artifactcleaner.cpp",
"artifactcleaner.h",
- "artifactlist.cpp",
- "artifactlist.h",
+ "artifactset.cpp",
+ "artifactset.h",
"artifactvisitor.cpp",
"artifactvisitor.h",
- "automoc.cpp",
- "automoc.h",
"buildgraph.cpp",
"buildgraph.h",
+ "buildgraphnode.cpp",
+ "buildgraphnode.h",
"buildgraphloader.cpp",
"buildgraphloader.h",
+ "buildgraphvisitor.h",
"command.cpp",
"command.h",
"cycledetector.cpp",
@@ -103,6 +104,10 @@ Product {
"inputartifactscanner.h",
"jscommandexecutor.cpp",
"jscommandexecutor.h",
+ "moctagscanner.cpp",
+ "moctagscanner.h",
+ "nodeset.cpp",
+ "nodeset.h",
"processcommandexecutor.cpp",
"processcommandexecutor.h",
"productbuilddata.cpp",
@@ -113,12 +118,16 @@ Product {
"projectbuilddata.h",
"rulegraph.cpp",
"rulegraph.h",
+ "rulenode.cpp",
+ "rulenode.h",
"rulesapplicator.cpp",
"rulesapplicator.h",
"rulesevaluationcontext.cpp",
"rulesevaluationcontext.h",
"scanresultcache.cpp",
"scanresultcache.h",
+ "tagscannerrunner.cpp",
+ "tagscannerrunner.h",
"timestampsupdater.cpp",
"timestampsupdater.h",
"transformer.cpp",