aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/reference/items/rule.qdoc29
-rw-r--r--src/lib/corelib/api/project.cpp25
-rw-r--r--src/lib/corelib/buildgraph/artifact.cpp49
-rw-r--r--src/lib/corelib/buildgraph/artifact.h37
-rw-r--r--src/lib/corelib/buildgraph/artifactcleaner.cpp2
-rw-r--r--src/lib/corelib/buildgraph/artifactset.cpp38
-rw-r--r--src/lib/corelib/buildgraph/artifactset.h70
-rw-r--r--src/lib/corelib/buildgraph/artifactvisitor.cpp23
-rw-r--r--src/lib/corelib/buildgraph/artifactvisitor.h9
-rw-r--r--src/lib/corelib/buildgraph/automoc.cpp345
-rw-r--r--src/lib/corelib/buildgraph/buildgraph.cpp103
-rw-r--r--src/lib/corelib/buildgraph/buildgraph.h23
-rw-r--r--src/lib/corelib/buildgraph/buildgraph.pri13
-rw-r--r--src/lib/corelib/buildgraph/buildgraphloader.cpp130
-rw-r--r--src/lib/corelib/buildgraph/buildgraphloader.h12
-rw-r--r--src/lib/corelib/buildgraph/buildgraphnode.cpp80
-rw-r--r--src/lib/corelib/buildgraph/buildgraphnode.h85
-rw-r--r--src/lib/corelib/buildgraph/buildgraphvisitor.h55
-rw-r--r--src/lib/corelib/buildgraph/command.cpp68
-rw-r--r--src/lib/corelib/buildgraph/command.h19
-rw-r--r--src/lib/corelib/buildgraph/cycledetector.cpp56
-rw-r--r--src/lib/corelib/buildgraph/cycledetector.h23
-rw-r--r--src/lib/corelib/buildgraph/executor.cpp548
-rw-r--r--src/lib/corelib/buildgraph/executor.h42
-rw-r--r--src/lib/corelib/buildgraph/executorjob.cpp4
-rw-r--r--src/lib/corelib/buildgraph/forward_decls.h9
-rw-r--r--src/lib/corelib/buildgraph/inputartifactscanner.cpp4
-rw-r--r--src/lib/corelib/buildgraph/jscommandexecutor.cpp2
-rw-r--r--src/lib/corelib/buildgraph/nodeset.cpp93
-rw-r--r--src/lib/corelib/buildgraph/nodeset.h122
-rw-r--r--src/lib/corelib/buildgraph/productbuilddata.cpp66
-rw-r--r--src/lib/corelib/buildgraph/productbuilddata.h27
-rw-r--r--src/lib/corelib/buildgraph/productinstaller.cpp2
-rw-r--r--src/lib/corelib/buildgraph/projectbuilddata.cpp268
-rw-r--r--src/lib/corelib/buildgraph/projectbuilddata.h10
-rw-r--r--src/lib/corelib/buildgraph/qtmocscanner.cpp189
-rw-r--r--src/lib/corelib/buildgraph/qtmocscanner.h (renamed from src/lib/corelib/buildgraph/automoc.h)73
-rw-r--r--src/lib/corelib/buildgraph/rescuableartifactdata.cpp70
-rw-r--r--src/lib/corelib/buildgraph/rescuableartifactdata.h72
-rw-r--r--src/lib/corelib/buildgraph/rulegraph.cpp62
-rw-r--r--src/lib/corelib/buildgraph/rulegraph.h13
-rw-r--r--src/lib/corelib/buildgraph/rulenode.cpp144
-rw-r--r--src/lib/corelib/buildgraph/rulenode.h76
-rw-r--r--src/lib/corelib/buildgraph/rulesapplicator.cpp178
-rw-r--r--src/lib/corelib/buildgraph/rulesapplicator.h26
-rw-r--r--src/lib/corelib/buildgraph/timestampsupdater.cpp2
-rw-r--r--src/lib/corelib/buildgraph/transformer.cpp44
-rw-r--r--src/lib/corelib/buildgraph/transformer.h6
-rw-r--r--src/lib/corelib/buildgraph/tst_buildgraph.cpp6
-rw-r--r--src/lib/corelib/corelib.qbs13
-rw-r--r--src/lib/corelib/language/builtindeclarations.cpp9
-rw-r--r--src/lib/corelib/language/language.cpp171
-rw-r--r--src/lib/corelib/language/language.h29
-rw-r--r--src/lib/corelib/language/projectresolver.cpp20
-rw-r--r--src/lib/corelib/tools/persistence.cpp2
-rw-r--r--src/lib/qtprofilesetup/templates/core.qbs64
-rw-r--r--src/plugins/scanner/cpp/cppscanner.cpp2
-rw-r--r--tests/auto/api/tst_api.cpp23
-rw-r--r--tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/main.cpp7
-rw-r--r--tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/object.cpp13
-rw-r--r--tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/object.h4
-rw-r--r--tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/project.qbs7
-rw-r--r--tests/auto/blackbox/testdata/added-file-persistent/file.cpp1
-rw-r--r--tests/auto/blackbox/testdata/added-file-persistent/main.cpp3
-rw-r--r--tests/auto/blackbox/testdata/added-file-persistent/project.qbs8
-rw-r--r--tests/auto/blackbox/testdata/fileTagger/moc_cpp.qbs2
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp98
-rw-r--r--tests/auto/blackbox/tst_blackbox.h3
68 files changed, 2699 insertions, 1232 deletions
diff --git a/doc/reference/items/rule.qdoc b/doc/reference/items/rule.qdoc
index 35fc2aa5e..05fc22643 100644
--- a/doc/reference/items/rule.qdoc
+++ b/doc/reference/items/rule.qdoc
@@ -116,6 +116,13 @@
Unlike \a{inputs}, the property \a{auxiliaryInputs} has no effect on the content of the
\a{inputs} variable in the \a{prepare} script.
\row
+ \li excludedAuxiliaryInputs
+ \li string list
+ \li undefined
+ \li A list of file tags. Connections to rules that produce these file tags are prevented.
+ This property has no effect on the content of the \a{inputs} variable in the \a{prepare}
+ script.
+ \row
\li usings
\li string list
\li undefined
@@ -131,6 +138,28 @@
script. Also, each output artifact of this rule will be dependent on
those artifacts.
\row
+ \li outputArtifacts
+ \li array of objects
+ \li undefined
+ \li An array of output artifacts, specified as JavaScript objects.
+ Example:
+ \code
+ outputArtifacts: [{filePath: "myfile.txt", fileTags: ["foo", "bar"]}]
+ \endcode
+ For a description of the possible properties, see the documentation of the Artifact
+ item.
+ Output artifacts can be specified either by \c{Rule.outputArtifacts} or by \c{Artifact}
+ items. Use \c{Rule.outputArtifacts} if the set of outputs is not fixed but dependent on
+ the input's content.
+ The user may set the property \c{explicitlyDependsOn} on artifact objects, which is
+ similar to \c{Rule.explicitlyDependsOn}.
+ \row
+ \li outputFileTags
+ \li string list
+ \li undefined
+ \li If output artifacts are specified by \c{Rule.outputArtifacts}, then
+ \c{Rule.outputFileTags} must be a list of file tags the rule potentially produces.
+ \row
\li condition
\li bool
\li true
diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp
index b9e9b4583..99b2eeac0 100644
--- a/src/lib/corelib/api/project.cpp
+++ b/src/lib/corelib/api/project.cpp
@@ -438,18 +438,10 @@ void ProjectPrivate::addFiles(const ProductData &product, const GroupData &group
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;
+ groupContext.resolvedProduct->registerAddedArtifact(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;
@@ -521,14 +513,10 @@ void ProjectPrivate::removeFilesFromBuildGraph(const ResolvedProductConstPtr &pr
QBS_CHECK(internalProject->buildData);
foreach (const SourceArtifactPtr &sa, files) {
Artifact * const artifact = lookupArtifact(product, sa->absoluteFilePath);
- QBS_CHECK(artifact);
- internalProject->buildData->removeArtifactAndExclusiveDependents(artifact, logger);
+ if (artifact) // Can be null if the executor has not yet applied the respective rule.
+ internalProject->buildData->removeArtifactAndExclusiveDependents(artifact, logger);
+ delete artifact;
}
- RulesEvaluationContextPtr &evalContext
- = internalProject->buildData->evaluationContext;
- evalContext = QSharedPointer<RulesEvaluationContext>(new RulesEvaluationContext(logger));
- internalProject->buildData->updateNodesThatMustGetNewTransformer(logger);
- evalContext.clear();
}
static void updateLocationIfNecessary(CodeLocation &location, const CodeLocation &changeLocation,
@@ -611,7 +599,7 @@ void ProjectPrivate::retrieveProjectData(ProjectData &projectData,
product.d->groups << createGroupDataFromGroup(resolvedGroup);
if (resolvedProduct->enabled) {
QBS_CHECK(resolvedProduct->buildData);
- foreach (const Artifact * const a, resolvedProduct->buildData->targetArtifacts) {
+ foreach (const Artifact * const a, resolvedProduct->buildData->targetArtifacts()) {
TargetArtifact ta;
ta.d->filePath = a->filePath();
ta.d->fileTags = a->fileTags.toStringList();
@@ -864,7 +852,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/corelib/buildgraph/artifact.cpp b/src/lib/corelib/buildgraph/artifact.cpp
index c5203e5ea..d9f442494 100644
--- a/src/lib/corelib/buildgraph/artifact.cpp
+++ b/src/lib/corelib/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>
@@ -62,6 +62,19 @@ Artifact::Artifact()
Artifact::~Artifact()
{
+ foreach (Artifact *p, parentArtifacts())
+ p->childrenAddedByScanner.remove(this);
+}
+
+void Artifact::accept(BuildGraphVisitor *visitor)
+{
+ if (visitor->visit(this))
+ acceptChildren(visitor);
+}
+
+QString Artifact::toString() const
+{
+ return QLatin1String("ARTIFACT ") + filePath();
}
void Artifact::initialize()
@@ -71,15 +84,35 @@ void Artifact::initialize()
inputsScanned = false;
timestampRetrieved = false;
alwaysUpdated = true;
+ oldDataPossiblyPresent = true;
+}
+
+ArtifactSet Artifact::parentArtifacts() const
+{
+ return ArtifactSet::fromNodeSet(parents);
+}
+
+ArtifactSet Artifact::childArtifacts() const
+{
+ return ArtifactSet::fromNodeSet(children);
+}
+
+void Artifact::onChildDisconnected(BuildGraphNode *child)
+{
+ Artifact *childArtifact = dynamic_cast<Artifact *>(child);
+ if (!childArtifact)
+ return;
+ childrenAddedByScanner.remove(childArtifact);
}
void Artifact::load(PersistentPool &pool)
{
FileResourceBase::load(pool);
- pool.loadContainer(children);
+ BuildGraphNode::load(pool);
+ children.load(pool);
// restore parents of the loaded children
- for (ArtifactSet::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,16 +123,18 @@ void Artifact::load(PersistentPool &pool)
pool.stream()
>> fileTags
>> artifactType
- >> autoMocTimestamp
>> c;
alwaysUpdated = c;
+ pool.stream() >> c;
+ oldDataPossiblyPresent = c;
}
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,8 +142,8 @@ void Artifact::store(PersistentPool &pool) const
pool.stream()
<< fileTags
<< artifactType
- << autoMocTimestamp
- << static_cast<unsigned char>(alwaysUpdated);
+ << static_cast<unsigned char>(alwaysUpdated)
+ << static_cast<unsigned char>(oldDataPossiblyPresent);
}
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/artifact.h b/src/lib/corelib/buildgraph/artifact.h
index 93cfeec52..ca62daea7 100644
--- a/src/lib/corelib/buildgraph/artifact.h
+++ b/src/lib/corelib/buildgraph/artifact.h
@@ -32,12 +32,10 @@
#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,19 @@ class Logger;
*
*
*/
-class Artifact : public FileResourceBase
+class Artifact : public FileResourceBase, public BuildGraphNode
{
public:
Artifact();
~Artifact();
- ArtifactSet parents;
- ArtifactSet children;
+ Type type() const { return ArtifactNodeType; }
+ void accept(BuildGraphVisitor *visitor);
+ QString toString() const;
+
ArtifactSet childrenAddedByScanner;
QSet<FileDependency *> fileDependencies;
FileTags fileTags;
- WeakPointer<ResolvedProduct> product;
TransformerPtr transformer;
PropertyMapPtr properties;
@@ -76,22 +75,16 @@ 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;
+ bool oldDataPossiblyPresent : 1;
void initialize();
+ ArtifactSet parentArtifacts() const;
+ ArtifactSet childArtifacts() const;
+ void onChildDisconnected(BuildGraphNode *child);
private:
void load(PersistentPool &pool);
@@ -113,16 +106,16 @@ inline QString toString(Artifact::ArtifactType t)
}
// debugging helper
-inline QString toString(Artifact::BuildState s)
+inline QString toString(BuildGraphNode::BuildState s)
{
switch (s) {
- case Artifact::Untouched:
+ case BuildGraphNode::Untouched:
return QLatin1String("Untouched");
- case Artifact::Buildable:
+ case BuildGraphNode::Buildable:
return QLatin1String("Buildable");
- case Artifact::Building:
+ case BuildGraphNode::Building:
return QLatin1String("Building");
- case Artifact::Built:
+ case BuildGraphNode::Built:
return QLatin1String("Built");
default:
return QLatin1String("Unknown");
diff --git a/src/lib/corelib/buildgraph/artifactcleaner.cpp b/src/lib/corelib/buildgraph/artifactcleaner.cpp
index a1e91cdaf..ad93c1d4a 100644
--- a/src/lib/corelib/buildgraph/artifactcleaner.cpp
+++ b/src/lib/corelib/buildgraph/artifactcleaner.cpp
@@ -113,7 +113,7 @@ private:
if (m_options.cleanType() == CleanOptions::CleanupTemporaries) {
QBS_CHECK(artifact->transformer);
foreach (Artifact * const sibling, artifact->transformer->outputs) {
- if (artifact->product->buildData->targetArtifacts.contains(sibling))
+ if (artifact->product->buildData->targetArtifacts().contains(sibling))
return;
}
}
diff --git a/src/lib/corelib/buildgraph/artifactset.cpp b/src/lib/corelib/buildgraph/artifactset.cpp
index 921de6ee0..c44f5e4b9 100644
--- a/src/lib/corelib/buildgraph/artifactset.cpp
+++ b/src/lib/corelib/buildgraph/artifactset.cpp
@@ -28,30 +28,44 @@
****************************************************************************/
#include "artifactset.h"
+#include "artifact.h"
namespace qbs {
namespace Internal {
ArtifactSet::ArtifactSet()
-{}
+{
+}
ArtifactSet::ArtifactSet(const ArtifactSet &other)
- : m_data(other.m_data)
-{}
+ : QSet<Artifact *>(other)
+{
+}
+
+ArtifactSet::ArtifactSet(const QSet<Artifact *> &other)
+ : QSet<Artifact *>(other)
+{
+}
-ArtifactSet &ArtifactSet::unite(const ArtifactSet &other)
+ArtifactSet ArtifactSet::fromNodeSet(const NodeSet &nodes)
{
- std::set<Artifact *>::const_iterator it = other.m_data.begin();
- for (; it != other.m_data.end(); ++it)
- m_data.insert(*it);
- return *this;
+ ArtifactSet result;
+ result.reserve(nodes.count());
+ foreach (BuildGraphNode *node, nodes) {
+ Artifact *artifact = dynamic_cast<Artifact *>(node);
+ if (artifact)
+ result += artifact;
+ }
+ return result;
}
-void ArtifactSet::remove(Artifact *artifact)
+ArtifactSet ArtifactSet::fromNodeList(const QList<Artifact *> &lst)
{
- iterator it = m_data.find(artifact);
- if (it != m_data.end())
- m_data.erase(it);
+ ArtifactSet result;
+ result.reserve(lst.count());
+ for (QList<Artifact *>::const_iterator it = lst.constBegin(); it != lst.end(); ++it)
+ result.insert(*it);
+ return result;
}
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/artifactset.h b/src/lib/corelib/buildgraph/artifactset.h
index fccff34d9..092e9e8a2 100644
--- a/src/lib/corelib/buildgraph/artifactset.h
+++ b/src/lib/corelib/buildgraph/artifactset.h
@@ -30,80 +30,22 @@
#ifndef QBS_ARTIFACTSET_H
#define QBS_ARTIFACTSET_H
-#include <set>
-#include <cstddef>
+#include <QSet>
namespace qbs {
namespace Internal {
class Artifact;
+class NodeSet;
-/**
- * Set that holds a bunch of build graph artifacts.
- * This is faster than QSet when iterating over the container.
- */
-class ArtifactSet
+class ArtifactSet : public QSet<Artifact *>
{
public:
ArtifactSet();
ArtifactSet(const ArtifactSet &other);
-
- ArtifactSet &unite(const ArtifactSet &other);
-
- typedef std::set<Artifact *>::const_iterator const_iterator;
- typedef std::set<Artifact *>::iterator iterator;
- typedef Artifact * value_type;
-
- iterator begin() { return m_data.begin(); }
- iterator end() { return m_data.end(); }
- const_iterator begin() const { return m_data.begin(); }
- const_iterator end() const { return m_data.end(); }
- const_iterator constBegin() const { return m_data.begin(); }
- const_iterator constEnd() const { return m_data.end(); }
-
- void insert(Artifact *artifact)
- {
- m_data.insert(artifact);
- }
-
- void operator +=(Artifact *artifact)
- {
- insert(artifact);
- }
-
- void remove(Artifact *artifact);
-
- bool contains(Artifact *artifact) const
- {
- return m_data.find(artifact) != m_data.end();
- }
-
- void clear()
- {
- m_data.clear();
- }
-
- bool isEmpty() const
- {
- return m_data.empty();
- }
-
- int count() const
- {
- return (int)m_data.size();
- }
-
- void reserve(int)
- {
- // no-op
- }
-
- bool operator==(const ArtifactSet &other) const { return m_data == other.m_data; }
- bool operator!=(const ArtifactSet &other) const { return !(*this == other); }
-
-
-private:
- std::set<Artifact *> m_data;
+ ArtifactSet(const QSet<Artifact *> &other);
+ static ArtifactSet fromNodeSet(const NodeSet &nodes);
+ static ArtifactSet fromNodeList(const QList<Artifact *> &lst);
};
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/artifactvisitor.cpp b/src/lib/corelib/buildgraph/artifactvisitor.cpp
index 3b5203e26..e8f438c9b 100644
--- a/src/lib/corelib/buildgraph/artifactvisitor.cpp
+++ b/src/lib/corelib/buildgraph/artifactvisitor.cpp
@@ -40,27 +40,26 @@ ArtifactVisitor::ArtifactVisitor(int artifactType) : m_artifactType(artifactType
{
}
-void ArtifactVisitor::visitArtifact(Artifact *artifact)
-{
- QBS_CHECK(artifact);
- if (m_artifactType & artifact->artifactType)
- doVisit(artifact);
-}
-
void ArtifactVisitor::visitProduct(const ResolvedProductConstPtr &product)
{
if (!product->buildData)
return;
- foreach (Artifact * const artifact, product->buildData->artifacts)
- visitArtifact(artifact);
+ foreach (BuildGraphNode *node, product->buildData->nodes)
+ node->accept(this);
}
void ArtifactVisitor::visitProject(const ResolvedProjectConstPtr &project)
{
- foreach (const ResolvedProductConstPtr &product, project->products)
+ foreach (const ResolvedProductConstPtr &product, project->allProducts())
visitProduct(product);
- foreach (const ResolvedProjectConstPtr &subProject, project->subProjects)
- visitProject(subProject);
+}
+
+bool ArtifactVisitor::visit(Artifact *artifact)
+{
+ QBS_CHECK(artifact);
+ if (m_artifactType & artifact->artifactType)
+ doVisit(artifact);
+ return false;
}
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/artifactvisitor.h b/src/lib/corelib/buildgraph/artifactvisitor.h
index 0c113ea1b..2cd67a8f8 100644
--- a/src/lib/corelib/buildgraph/artifactvisitor.h
+++ b/src/lib/corelib/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/corelib/buildgraph/automoc.cpp b/src/lib/corelib/buildgraph/automoc.cpp
deleted file mode 100644
index 13c24674a..000000000
--- a/src/lib/corelib/buildgraph/automoc.cpp
+++ /dev/null
@@ -1,345 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 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(Tr::tr("C++ scanner cannot be loaded."));
-
- Artifact *pluginMetaDataFile = 0;
- Artifact *pchFile = 0;
- QList<QPair<Artifact *, FileType> > artifactsToMoc;
- QSet<QString> includedMocCppFiles;
- const FileTime currentTime = FileTime::currentTime();
- ArtifactSet::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 = QLatin1String("moc_") + FileInfo::baseName(artifact->filePath()) +
- QLatin1String(".cpp");
- break;
- case CppFileType:
- mocFileName = FileInfo::baseName(artifact->filePath()) + QLatin1String(".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(QLatin1String("moc_")) &&
- includedFilePath.endsWith(QLatin1String(".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/corelib/buildgraph/buildgraph.cpp b/src/lib/corelib/buildgraph/buildgraph.cpp
index 252f636cf..dd36ee15c 100644
--- a/src/lib/corelib/buildgraph/buildgraph.cpp
+++ b/src/lib/corelib/buildgraph/buildgraph.cpp
@@ -216,14 +216,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 (ArtifactSet::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;
@@ -242,28 +242,34 @@ 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);
- foreach (const Artifact * const child, p->children)
- if (child != c && child->filePath() == c->filePath())
- throw ErrorInfo(QString::fromLocal8Bit("Artifact %1 already has a child artifact %2 as different object.").arg(p->filePath(), c->filePath()), CodeLocation(), true);
+ if (Artifact *ac = dynamic_cast<Artifact *>(c)) {
+ foreach (const Artifact * const child, ArtifactSet::fromNodeSet(p->children))
+ if (child != ac && child->filePath() == ac->filePath()) {
+ throw ErrorInfo(QString::fromLocal8Bit("%1 already has a child artifact %2 as "
+ "different object.").arg(p->toString(),
+ ac->filePath()),
+ CodeLocation(), true);
+ }
+ }
p->children.insert(c);
c->parents.insert(p);
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_impl(Artifact *u, Artifact *v, QSet<Artifact *> *seen)
+static bool existsPath_impl(BuildGraphNode *u, BuildGraphNode *v, QSet<BuildGraphNode *> *seen)
{
if (u == v)
return true;
@@ -272,19 +278,27 @@ static bool existsPath_impl(Artifact *u, Artifact *v, QSet<Artifact *> *seen)
return false;
seen->insert(u);
- for (ArtifactSet::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_impl(*it, v, seen))
return true;
return false;
}
-static bool existsPath(Artifact *u, Artifact *v)
+static bool existsPath(BuildGraphNode *u, BuildGraphNode *v)
{
- QSet<Artifact *> seen;
+ QSet<BuildGraphNode *> seen;
return existsPath_impl(u, v, &seen);
}
+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);
@@ -294,7 +308,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;
@@ -304,31 +318,32 @@ 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);
+ u->onChildDisconnected(v);
}
void removeGeneratedArtifactFromDisk(Artifact *artifact, const Logger &logger)
{
if (artifact->artifactType != Artifact::Generated)
return;
+ removeGeneratedArtifactFromDisk(artifact->filePath(), logger);
+}
- QFile file(artifact->filePath());
+void removeGeneratedArtifactFromDisk(const QString &filePath, const Logger &logger)
+{
+ QFile file(filePath);
if (!file.exists())
return;
-
- logger.qbsDebug() << "removing " << artifact->fileName();
- if (!file.remove()) {
- logger.qbsWarning() << QString::fromLocal8Bit("Cannot remove '%1'.")
- .arg(artifact->filePath());
- }
+ logger.qbsDebug() << "removing " << filePath;
+ if (!file.remove())
+ logger.qbsWarning() << QString::fromLocal8Bit("Cannot remove '%1'.").arg(filePath);
}
QString relativeArtifactFileName(const Artifact *artifact)
@@ -404,7 +419,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())) {
@@ -421,7 +436,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;
@@ -443,25 +458,31 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product, con
QBS_CHECK(!!product->enabled == !!buildData);
if (!product->enabled)
return;
- foreach (Artifact * const ta, buildData->targetArtifacts) {
+ foreach (BuildGraphNode * const node, buildData->roots) {
if (logger.traceEnabled())
- logger.qbsTrace() << "Checking target artifact '" << ta->fileName() << "'.";
- QBS_CHECK(buildData->artifacts.contains(ta));
+ logger.qbsTrace() << "Checking root node '" << node->toString() << "'.";
+ QBS_CHECK(buildData->nodes.contains(node));
}
QSet<QString> filePaths;
- foreach (Artifact * const artifact, buildData->artifacts) {
- logger.qbsDebug() << "Sanity checking artifact '" << artifact->fileName() << "'";
+ foreach (BuildGraphNode * const node, buildData->nodes) {
+ logger.qbsDebug() << "Sanity checking node '" << node->toString() << "'";
+ QBS_CHECK(node->product == product);
+ foreach (const BuildGraphNode * const parent, node->parents)
+ QBS_CHECK(parent->children.contains(node));
+ foreach (BuildGraphNode * const child, node->children) {
+ QBS_CHECK(child->parents.contains(node));
+ QBS_CHECK(child->product);
+ QBS_CHECK(!child->product->buildData.isNull());
+ QBS_CHECK(child->product->buildData->nodes.contains(child));
+ }
+
+ Artifact * const artifact = dynamic_cast<Artifact *>(node);
+ if (!artifact)
+ continue;
+
QBS_CHECK(!filePaths.contains(artifact->filePath()));
filePaths << artifact->filePath();
- QBS_CHECK(artifact->product == product);
- foreach (const Artifact * const parent, artifact->parents)
- QBS_CHECK(parent->children.contains(artifact));
- foreach (Artifact * const child, artifact->children) {
- QBS_CHECK(child->parents.contains(artifact));
- QBS_CHECK(!child->product.isNull());
- QBS_CHECK(child->product->buildData);
- QBS_CHECK(child->product->buildData->artifacts.contains(child));
- }
+
foreach (Artifact * const child, artifact->childrenAddedByScanner)
QBS_CHECK(artifact->children.contains(child));
const TransformerConstPtr transformer = artifact->transformer;
@@ -475,9 +496,9 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product, con
ArtifactSet transformerOutputChildren;
foreach (const Artifact * const output, transformer->outputs) {
QBS_CHECK(output->transformer == transformer);
- transformerOutputChildren.unite(output->children);
+ transformerOutputChildren.unite(ArtifactSet::fromNodeSet(output->children));
QSet<QString> childFilePaths;
- foreach (const Artifact * const a, output->children) {
+ foreach (const Artifact * const a, ArtifactSet::fromNodeSet(output->children)) {
if (childFilePaths.contains(a->filePath())) {
throw ErrorInfo(QString::fromLocal8Bit("There is more than one artifact for "
"file '%1' in the child list for output '%2'.")
diff --git a/src/lib/corelib/buildgraph/buildgraph.h b/src/lib/corelib/buildgraph/buildgraph.h
index 5244894cb..d0da37fa7 100644
--- a/src/lib/corelib/buildgraph/buildgraph.h
+++ b/src/lib/corelib/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 PrepareScriptObserver;
@@ -59,17 +58,16 @@ 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(Artifact *u, Artifact *v, const Logger &logger);
+void removeGeneratedArtifactFromDisk(const QString &filePath, const Logger &logger);
+void disconnect(BuildGraphNode *u, BuildGraphNode *v, const Logger &logger);
void setupScriptEngineForFile(ScriptEngine *engine, const ResolvedFileContextConstPtr &fileContext,
QScriptValue targetObject);
@@ -80,15 +78,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/corelib/buildgraph/buildgraph.pri b/src/lib/corelib/buildgraph/buildgraph.pri
index 4142d94bd..0674f2ca7 100644
--- a/src/lib/corelib/buildgraph/buildgraph.pri
+++ b/src/lib/corelib/buildgraph/buildgraph.pri
@@ -4,9 +4,9 @@ SOURCES += \
$$PWD/artifactcleaner.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,11 +14,15 @@ SOURCES += \
$$PWD/filedependency.cpp \
$$PWD/inputartifactscanner.cpp \
$$PWD/jscommandexecutor.cpp \
+ $$PWD/nodeset.cpp \
$$PWD/processcommandexecutor.cpp \
$$PWD/productbuilddata.cpp \
$$PWD/productinstaller.cpp \
$$PWD/projectbuilddata.cpp \
+ $$PWD/qtmocscanner.cpp \
+ $$PWD/rescuableartifactdata.cpp \
$$PWD/rulegraph.cpp \
+ $$PWD/rulenode.cpp \
$$PWD/rulesapplicator.cpp \
$$PWD/rulesevaluationcontext.cpp \
$$PWD/scanresultcache.cpp \
@@ -31,9 +35,10 @@ HEADERS += \
$$PWD/artifactcleaner.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,11 +47,15 @@ HEADERS += \
$$PWD/forward_decls.h \
$$PWD/inputartifactscanner.h \
$$PWD/jscommandexecutor.h \
+ $$PWD/nodeset.h \
$$PWD/processcommandexecutor.h \
$$PWD/productbuilddata.h \
$$PWD/productinstaller.h \
$$PWD/projectbuilddata.h \
+ $$PWD/qtmocscanner.h \
+ $$PWD/rescuableartifactdata.h \
$$PWD/rulegraph.h \
+ $$PWD/rulenode.h \
$$PWD/rulesapplicator.h \
$$PWD/rulesevaluationcontext.h \
$$PWD/scanresultcache.h \
diff --git a/src/lib/corelib/buildgraph/buildgraphloader.cpp b/src/lib/corelib/buildgraph/buildgraphloader.cpp
index 1a7d0a7f1..21cdc9547 100644
--- a/src/lib/corelib/buildgraph/buildgraphloader.cpp
+++ b/src/lib/corelib/buildgraph/buildgraphloader.cpp
@@ -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) {
@@ -217,8 +219,11 @@ 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, ChildrenInfo(ArtifactSet::fromNodeSet(a->children),
+ a->childrenAddedByScanner));
+ }
}
}
@@ -248,7 +253,8 @@ 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);
@@ -258,8 +264,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.
@@ -495,7 +501,7 @@ bool BuildGraphLoader::checkTransformersForPropertyChanges(const ResolvedProduct
{
bool transformerChanges = false;
QSet<TransformerConstPtr> seenTransformers;
- foreach (Artifact * const artifact, restoredProduct->buildData->artifacts) {
+ foreach (Artifact *artifact, ArtifactSet::fromNodeSet(restoredProduct->buildData->nodes)) {
const TransformerPtr transformer = artifact->transformer;
if (!transformer || seenTransformers.contains(transformer))
continue;
@@ -517,7 +523,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);
}
}
@@ -529,7 +535,6 @@ void BuildGraphLoader::onProductFileListChanged(const ResolvedProductPtr &restor
QBS_CHECK(newlyResolvedProduct->enabled);
- ArtifactsPerFileTagMap artifactsPerFileTag;
QList<Artifact *> addedArtifacts;
ArtifactSet artifactsToRemove;
QHash<QString, SourceArtifactConstPtr> oldArtifacts, newArtifacts;
@@ -601,20 +606,13 @@ void BuildGraphLoader::onProductFileListChanged(const ResolvedProductPtr &restor
// handle added filetags
foreach (const FileTag &addedFileTag, changedArtifact->fileTags - a->fileTags) {
artifact->fileTags += addedFileTag;
- artifactsPerFileTag[addedFileTag] += artifact;
+ newlyResolvedProduct->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);
- }
- }
+ newlyResolvedProduct->registerRemovedFileTag(removedFileTag, artifact);
}
}
@@ -631,23 +629,11 @@ void BuildGraphLoader::onProductFileListChanged(const ResolvedProductPtr &restor
}
}
- // apply rules for new artifacts
- foreach (Artifact *artifact, addedArtifacts)
- foreach (const FileTag &ft, artifact->fileTags)
- artifactsPerFileTag[ft] += artifact;
- 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)
- newlyResolvedProduct->topLevelProject()->buildData->artifactsThatMustGetNewTransformers += parent;
- newlyResolvedProduct->topLevelProject()->buildData->updateNodesThatMustGetNewTransformer(m_logger);
-
// defer destruction of removed artifacts
foreach (Artifact *artifact, artifactsToRemove)
m_objectsToDelete << artifact;
+ foreach (Artifact * const artifact, addedArtifacts)
+ newlyResolvedProduct->registerAddedArtifact(artifact);
}
static SourceArtifactConstPtr findSourceArtifact(const ResolvedProductConstPtr &product,
@@ -673,7 +659,7 @@ bool BuildGraphLoader::checkForPropertyChanges(const TransformerPtr &restoredTra
const QVariantMap properties = property.kind == Property::PropertyInProject
? freshProduct->project->projectProperties() : freshProduct->properties->value();
if (checkForPropertyChange(property, properties)) {
- JavaScriptCommand * const pseudoCommand = new JavaScriptCommand;
+ const JavaScriptCommandPtr &pseudoCommand = JavaScriptCommand::create();
pseudoCommand->setSourceCode(QLatin1String("random stuff that will cause "
"commandsEqual() to fail"));
restoredTrafo->commands << pseudoCommand;
@@ -746,7 +732,7 @@ void BuildGraphLoader::replaceFileDependencyWithArtifact(const ResolvedProductPt
foreach (const ResolvedProductPtr &product, fileDepProduct->topLevelProject()->allProducts()) {
if (!product->buildData)
continue;
- foreach (Artifact *artifactInProduct, product->buildData->artifacts) {
+ foreach (Artifact *artifactInProduct, ArtifactSet::fromNodeSet(product->buildData->nodes)) {
if (artifactInProduct->fileDependencies.contains(filedep)) {
artifactInProduct->fileDependencies.remove(filedep);
loggedConnect(artifactInProduct, artifact, m_logger);
@@ -758,24 +744,9 @@ void BuildGraphLoader::replaceFileDependencyWithArtifact(const ResolvedProductPt
m_objectsToDelete << filedep;
}
-static bool commandsEqual(const TransformerConstPtr &t1, const TransformerConstPtr &t2)
-{
- if (t1->commands.count() != t2->commands.count())
- return false;
- for (int i = 0; i < t1->commands.count(); ++i)
- if (!t1->commands.at(i)->equals(t2->commands.at(i)))
- return false;
- return true;
-}
-
-/**
- * Rescues the following data from the restoredProduct to newlyResolvedProduct:
- * - dependencies between artifacts,
- * - time stamps of artifacts, if their commands have not changed.
- */
void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restoredProduct,
- const ResolvedProductPtr &newlyResolvedProduct,
- const ProjectBuildData *oldBuildData, const ChildListHash &childLists)
+ const ResolvedProductPtr &newlyResolvedProduct, ProjectBuildData *oldBuildData,
+ const ChildListHash &childLists)
{
if (!restoredProduct->enabled || !newlyResolvedProduct->enabled)
return;
@@ -785,22 +756,31 @@ void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restore
"product '%1'").arg(restoredProduct->name);
}
- foreach (Artifact *artifact, newlyResolvedProduct->buildData->artifacts) {
+ // This is needed for artifacts created by manually added transformers, which are
+ // already present in the build graph.
+ // FIXME: This complete block could go away if Transformers were also to create their
+ // output artifacts at build time.
+ QList<Artifact *> oldArtifactsCreatedByTransformers;
+ foreach (Artifact *artifact,
+ ArtifactSet::fromNodeSet(newlyResolvedProduct->buildData->nodes)) {
+ if (!artifact->transformer)
+ continue;
if (m_logger.traceEnabled()) {
m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] artifact '%1'")
.arg(artifact->fileName());
}
Artifact * const oldArtifact = lookupArtifact(restoredProduct, oldBuildData,
- artifact->dirPath(), artifact->fileName(), true);
+ artifact->dirPath(), artifact->fileName(),
+ true);
if (!oldArtifact || !oldArtifact->transformer) {
if (m_logger.traceEnabled())
m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] no transformer data");
continue;
}
-
- if (artifact->transformer
- && !commandsEqual(artifact->transformer, oldArtifact->transformer)) {
+ oldArtifactsCreatedByTransformers << oldArtifact;
+ if (!commandListsAreEqual(artifact->transformer->commands,
+ oldArtifact->transformer->commands)) {
if (m_logger.traceEnabled())
m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] artifact invalidated");
removeGeneratedArtifactFromDisk(oldArtifact, m_logger);
@@ -808,30 +788,40 @@ void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restore
}
artifact->setTimestamp(oldArtifact->timestamp());
- foreach (Artifact * const oldChild, childLists.value(oldArtifact)) {
+ const ChildrenInfo &childrenInfo = childLists.value(oldArtifact);
+ foreach (Artifact * const oldChild, childrenInfo.children) {
Artifact * const newChild = lookupArtifact(oldChild->product,
newlyResolvedProduct->topLevelProject()->buildData.data(),
oldChild->filePath(), true);
if (!newChild || artifact->children.contains(newChild))
continue;
safeConnect(artifact, newChild, m_logger);
+ if (childrenInfo.childrenAddedByScanner.contains(oldChild))
+ artifact->childrenAddedByScanner << newChild;
}
}
-}
-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;
+ // This is needed for artifacts created by rules, which happens later in the executor.
+ foreach (Artifact * const oldArtifact,
+ ArtifactSet::fromNodeSet(restoredProduct->buildData->nodes)) {
+ if (!oldArtifact->transformer)
+ continue;
+ if (oldArtifactsCreatedByTransformers.contains(oldArtifact))
+ continue;
+ Artifact * const newArtifact = lookupArtifact(newlyResolvedProduct, oldArtifact, false);
+ if (!newArtifact) {
+ RescuableArtifactData rad;
+ rad.timeStamp = oldArtifact->timestamp();
+ rad.commands = oldArtifact->transformer->commands;
+ const ChildrenInfo &childrenInfo = childLists.value(oldArtifact);
+ foreach (Artifact * const child, childrenInfo.children) {
+ rad.children << RescuableArtifactData::ChildData(child->product->name,
+ child->filePath(), childrenInfo.childrenAddedByScanner.contains(child));
+ }
+ newlyResolvedProduct->buildData->rescuableArtifactData.insert(
+ oldArtifact->filePath(), rad);
}
}
- if (product->buildData->targetArtifacts.isEmpty()) {
- const QString msg = QString::fromLocal8Bit("No artifacts generated for product '%1'.");
- logger.qbsDebug() << msg.arg(product->name);
- }
}
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/buildgraphloader.h b/src/lib/corelib/buildgraph/buildgraphloader.h
index 0c0b7cb8b..a6b705d1f 100644
--- a/src/lib/corelib/buildgraph/buildgraphloader.h
+++ b/src/lib/corelib/buildgraph/buildgraphloader.h
@@ -45,6 +45,7 @@ namespace Internal {
class FileDependency;
class FileResourceBase;
class FileTime;
+class NodeSet;
class Property;
class BuildGraphLoadResult
@@ -101,10 +102,17 @@ private:
void replaceFileDependencyWithArtifact(const ResolvedProductPtr &fileDepProduct,
FileDependency *filedep, Artifact *artifact);
- typedef QHash<const Artifact *, ArtifactSet> ChildListHash;
+ struct ChildrenInfo {
+ ChildrenInfo() {}
+ ChildrenInfo(const ArtifactSet &c1, const ArtifactSet &c2)
+ : children(c1), childrenAddedByScanner(c2) {}
+ ArtifactSet children;
+ ArtifactSet childrenAddedByScanner;
+ };
+ typedef QHash<const Artifact *, ChildrenInfo> ChildListHash;
void rescueOldBuildData(const ResolvedProductConstPtr &restoredProduct,
const ResolvedProductPtr &newlyResolvedProduct,
- const ProjectBuildData *oldBuildData,
+ ProjectBuildData *oldBuildData,
const ChildListHash &childLists);
RulesEvaluationContextPtr m_evalContext;
diff --git a/src/lib/corelib/buildgraph/buildgraphnode.cpp b/src/lib/corelib/buildgraph/buildgraphnode.cpp
new file mode 100644
index 000000000..d7eaac079
--- /dev/null
+++ b/src/lib/corelib/buildgraph/buildgraphnode.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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()
+{
+ foreach (BuildGraphNode *p, parents)
+ p->children.remove(this);
+ foreach (BuildGraphNode *c, children)
+ c->parents.remove(this);
+}
+
+void BuildGraphNode::onChildDisconnected(BuildGraphNode *child)
+{
+ Q_UNUSED(child);
+}
+
+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/corelib/buildgraph/buildgraphnode.h b/src/lib/corelib/buildgraph/buildgraphnode.h
new file mode 100644
index 000000000..dbb046547
--- /dev/null
+++ b/src/lib/corelib/buildgraph/buildgraphnode.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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.
+
+ enum Type
+ {
+ ArtifactNodeType,
+ RuleNodeType
+ };
+
+ virtual Type type() const = 0;
+ virtual void accept(BuildGraphVisitor *visitor) = 0;
+ virtual QString toString() const = 0;
+ virtual void onChildDisconnected(BuildGraphNode *child);
+
+protected:
+ explicit BuildGraphNode();
+ void acceptChildren(BuildGraphVisitor *visitor);
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_BUILDGRAPHNODE_H
diff --git a/src/lib/corelib/buildgraph/buildgraphvisitor.h b/src/lib/corelib/buildgraph/buildgraphvisitor.h
new file mode 100644
index 000000000..0a574e910
--- /dev/null
+++ b/src/lib/corelib/buildgraph/buildgraphvisitor.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 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(RuleNode *) { return true; }
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_BUILDGRAPHVISITOR_H
diff --git a/src/lib/corelib/buildgraph/command.cpp b/src/lib/corelib/buildgraph/command.cpp
index 60ec93907..ea47191c5 100644
--- a/src/lib/corelib/buildgraph/command.cpp
+++ b/src/lib/corelib/buildgraph/command.cpp
@@ -50,16 +50,16 @@ AbstractCommand::~AbstractCommand()
{
}
-AbstractCommand *AbstractCommand::createByType(AbstractCommand::CommandType commandType)
+AbstractCommandPtr AbstractCommand::createByType(AbstractCommand::CommandType commandType)
{
switch (commandType) {
case AbstractCommand::ProcessCommandType:
- return new ProcessCommand;
+ return ProcessCommand::create();
case AbstractCommand::JavaScriptCommandType:
- return new JavaScriptCommand;
+ return JavaScriptCommand::create();
}
qFatal("%s: Invalid command type %d", Q_FUNC_INFO, commandType);
- return 0;
+ return AbstractCommandPtr();
}
bool AbstractCommand::equals(const AbstractCommand *other) const
@@ -104,33 +104,33 @@ static QScriptValue js_Command(QScriptContext *context, QScriptEngine *engine)
if (Q_UNLIKELY(!context->isCalledAsConstructor()))
return context->throwError(Tr::tr("Command constructor called without new."));
- static ProcessCommand commandPrototype;
+ static ProcessCommandPtr commandPrototype = ProcessCommand::create();
QScriptValue program = context->argument(0);
if (program.isUndefined())
- program = engine->toScriptValue(commandPrototype.program());
+ program = engine->toScriptValue(commandPrototype->program());
QScriptValue arguments = context->argument(1);
if (arguments.isUndefined())
- arguments = engine->toScriptValue(commandPrototype.arguments());
+ arguments = engine->toScriptValue(commandPrototype->arguments());
QScriptValue cmd = js_CommandBase(context, engine);
cmd.setProperty(QLatin1String("className"),
engine->toScriptValue(QString::fromLatin1("Command")));
cmd.setProperty(QLatin1String("program"), program);
cmd.setProperty(QLatin1String("arguments"), arguments);
cmd.setProperty(QLatin1String("workingDir"),
- engine->toScriptValue(commandPrototype.workingDir()));
+ engine->toScriptValue(commandPrototype->workingDir()));
cmd.setProperty(QLatin1String("maxExitCode"),
- engine->toScriptValue(commandPrototype.maxExitCode()));
+ engine->toScriptValue(commandPrototype->maxExitCode()));
cmd.setProperty(QLatin1String("stdoutFilterFunction"),
- engine->toScriptValue(commandPrototype.stdoutFilterFunction()));
+ engine->toScriptValue(commandPrototype->stdoutFilterFunction()));
cmd.setProperty(QLatin1String("stderrFilterFunction"),
- engine->toScriptValue(commandPrototype.stderrFilterFunction()));
+ engine->toScriptValue(commandPrototype->stderrFilterFunction()));
cmd.setProperty(QLatin1String("responseFileThreshold"),
- engine->toScriptValue(commandPrototype.responseFileThreshold()));
+ engine->toScriptValue(commandPrototype->responseFileThreshold()));
cmd.setProperty(QLatin1String("responseFileUsagePrefix"),
- engine->toScriptValue(commandPrototype.responseFileUsagePrefix()));
+ engine->toScriptValue(commandPrototype->responseFileUsagePrefix()));
cmd.setProperty(QLatin1String("environment"),
- engine->toScriptValue(commandPrototype.environment().toStringList()));
+ engine->toScriptValue(commandPrototype->environment().toStringList()));
return cmd;
}
@@ -236,12 +236,12 @@ static QScriptValue js_JavaScriptCommand(QScriptContext *context, QScriptEngine
QLatin1String("JavaScriptCommand c'tor doesn't take arguments."));
}
- static JavaScriptCommand commandPrototype;
+ static JavaScriptCommandPtr commandPrototype = JavaScriptCommand::create();
QScriptValue cmd = js_CommandBase(context, engine);
cmd.setProperty(QLatin1String("className"),
engine->toScriptValue(QString::fromLatin1("JavaScriptCommand")));
cmd.setProperty(QLatin1String("sourceCode"),
- engine->toScriptValue(commandPrototype.sourceCode()));
+ engine->toScriptValue(commandPrototype->sourceCode()));
return cmd;
}
@@ -301,5 +301,41 @@ void JavaScriptCommand::store(QDataStream &s)
<< m_properties;
}
+QList<AbstractCommandPtr> loadCommandList(QDataStream &s)
+{
+ QList<AbstractCommandPtr> commands;
+ int count;
+ s >> count;
+ commands.reserve(count);
+ while (--count >= 0) {
+ int cmdType;
+ s >> cmdType;
+ const AbstractCommandPtr cmd
+ = AbstractCommand::createByType(static_cast<AbstractCommand::CommandType>(cmdType));
+ cmd->load(s);
+ commands += cmd;
+ }
+ return commands;
+}
+
+void storeCommandList(const QList<AbstractCommandPtr> &commands, QDataStream &s)
+{
+ s << commands.count();
+ foreach (const AbstractCommandPtr &cmd, commands) {
+ s << int(cmd->type());
+ cmd->store(s);
+ }
+}
+
+bool commandListsAreEqual(const QList<AbstractCommandPtr> &l1, const QList<AbstractCommandPtr> &l2)
+{
+ if (l1.count() != l2.count())
+ return false;
+ for (int i = 0; i < l1.count(); ++i)
+ if (!l1.at(i)->equals(l2.at(i).data()))
+ return false;
+ return true;
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/command.h b/src/lib/corelib/buildgraph/command.h
index 69fa32299..0b9b10edb 100644
--- a/src/lib/corelib/buildgraph/command.h
+++ b/src/lib/corelib/buildgraph/command.h
@@ -30,6 +30,8 @@
#ifndef QBS_COMMAND_H
#define QBS_COMMAND_H
+#include "forward_decls.h"
+
#include <tools/codelocation.h>
#include <QProcessEnvironment>
@@ -50,7 +52,7 @@ public:
JavaScriptCommandType
};
- static AbstractCommand *createByType(CommandType commandType);
+ static AbstractCommandPtr createByType(CommandType commandType);
static QString defaultDescription() { return QString(); }
static QString defaultHighLight() { return QString(); }
static bool defaultIsSilent() { return false; }
@@ -79,10 +81,9 @@ private:
class ProcessCommand : public AbstractCommand
{
public:
+ static ProcessCommandPtr create() { return ProcessCommandPtr(new ProcessCommand); }
static void setupForJavaScript(QScriptValue targetObject);
- ProcessCommand();
-
CommandType type() const { return ProcessCommandType; }
bool equals(const AbstractCommand *otherAbstractCommand) const;
void fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation);
@@ -100,6 +101,8 @@ public:
QProcessEnvironment environment() const { return m_environment; }
private:
+ ProcessCommand();
+
void getEnvironmentFromList(const QStringList &envList);
QString m_program;
@@ -116,10 +119,9 @@ private:
class JavaScriptCommand : public AbstractCommand
{
public:
+ static JavaScriptCommandPtr create() { return JavaScriptCommandPtr(new JavaScriptCommand); }
static void setupForJavaScript(QScriptValue targetObject);
- JavaScriptCommand();
-
virtual CommandType type() const { return JavaScriptCommandType; }
bool equals(const AbstractCommand *otherAbstractCommand) const;
void fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation);
@@ -132,10 +134,17 @@ public:
const QVariantMap &properties() const { return m_properties; }
private:
+ JavaScriptCommand();
+
QString m_sourceCode;
QVariantMap m_properties;
};
+QList<AbstractCommandPtr> loadCommandList(QDataStream &s);
+void storeCommandList(const QList<AbstractCommandPtr> &commands, QDataStream &s);
+
+bool commandListsAreEqual(const QList<AbstractCommandPtr> &l1, const QList<AbstractCommandPtr> &l2);
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/cycledetector.cpp b/src/lib/corelib/buildgraph/cycledetector.cpp
index 1ab6b4bb8..767fd2b12 100644
--- a/src/lib/corelib/buildgraph/cycledetector.cpp
+++ b/src/lib/corelib/buildgraph/cycledetector.cpp
@@ -30,7 +30,8 @@
#include "artifact.h"
#include "buildgraph.h"
-#include "productbuilddata.h"
+#include "projectbuilddata.h"
+#include "rulenode.h"
#include <language/language.h>
#include <logging/translator.h>
@@ -40,51 +41,58 @@ 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::visitProduct(const ResolvedProductConstPtr &product)
{
- if (!product->buildData)
- return;
- foreach (Artifact * const artifact, product->buildData->targetArtifacts)
- visitArtifact(artifact);
+ product->accept(this);
+}
+
+bool CycleDetector::visit(Artifact *artifact)
+{
+ return visitNode(artifact);
}
-void CycleDetector::visitArtifact(Artifact *artifact)
+bool CycleDetector::visit(RuleNode *ruleNode)
{
- if (Q_UNLIKELY(m_artifactsInCurrentPath.contains(artifact))) {
+ return visitNode(ruleNode);
+}
+
+bool CycleDetector::visitNode(BuildGraphNode *node)
+{
+ if (Q_UNLIKELY(m_nodesInCurrentPath.contains(node))) {
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(node))
+ error.append(node->toString());
throw error;
}
- if (m_allArtifacts.contains(artifact))
- return;
+ if (m_allNodes.contains(node))
+ return false;
- m_artifactsInCurrentPath += artifact;
- m_parent = artifact;
- foreach (Artifact * const child, artifact->children)
- visitArtifact(child);
- m_artifactsInCurrentPath -= artifact;
- m_allArtifacts += artifact;
+ m_nodesInCurrentPath += node;
+ m_parent = node;
+ foreach (BuildGraphNode * const child, node->children)
+ child->accept(this);
+ m_nodesInCurrentPath -= node;
+ m_allNodes += node;
+ return false;
}
-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/corelib/buildgraph/cycledetector.h b/src/lib/corelib/buildgraph/cycledetector.h
index 6e55efac3..690b3509e 100644
--- a/src/lib/corelib/buildgraph/cycledetector.h
+++ b/src/lib/corelib/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,23 +38,27 @@
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 visitProject(const TopLevelProjectConstPtr &project);
void visitProduct(const ResolvedProductConstPtr &product);
- void visitArtifact(Artifact *artifact);
private:
- void doVisit(Artifact *artifact);
+ bool visit(Artifact *artifact);
+ bool visit(RuleNode *ruleNode);
+
+ bool visitNode(BuildGraphNode *node);
- QList<Artifact *> cycle(Artifact *doubleEntry);
+ QList<BuildGraphNode *> cycle(BuildGraphNode *doubleEntry);
- QSet<Artifact *> m_allArtifacts;
- QSet<Artifact *> m_artifactsInCurrentPath;
- Artifact *m_parent;
+ QSet<BuildGraphNode *> m_allNodes;
+ QSet<BuildGraphNode *> m_nodesInCurrentPath;
+ BuildGraphNode *m_parent;
Logger m_logger;
};
diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp
index 070b37d97..02e0d332a 100644
--- a/src/lib/corelib/buildgraph/executor.cpp
+++ b/src/lib/corelib/buildgraph/executor.cpp
@@ -28,14 +28,15 @@
****************************************************************************/
#include "executor.h"
-#include "artifactvisitor.h"
-#include "automoc.h"
#include "buildgraph.h"
+#include "command.h"
#include "productbuilddata.h"
#include "projectbuilddata.h"
#include "cycledetector.h"
#include "executorjob.h"
#include "inputartifactscanner.h"
+#include "rescuableartifactdata.h"
+#include "rulenode.h"
#include "rulesevaluationcontext.h"
#include <buildgraph/transformer.h>
@@ -57,41 +58,7 @@
namespace qbs {
namespace Internal {
-class MocEffortCalculator : public ArtifactVisitor
-{
-public:
- MocEffortCalculator() : ArtifactVisitor(Artifact::SourceFile), m_effort(0) {}
-
- int effort() const { return m_effort; }
-
-private:
- void doVisit(Artifact *) { m_effort += 10; }
-
- int m_effort;
-};
-
-class BuildEffortCalculator : public ArtifactVisitor
-{
-public:
- BuildEffortCalculator() : ArtifactVisitor(Artifact::Generated), m_effort(0) {}
-
- int effort() const { return m_effort; }
-
- static int multiplier(const Artifact *artifact) {
- return artifact->transformer->rule->multiplex ? 200 : 20;
- }
-
-private:
- void doVisit(Artifact *artifact)
- {
- m_effort += multiplier(artifact);
- }
-
- int m_effort;
-};
-
-
-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 +73,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 +82,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 +187,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());
@@ -246,14 +209,11 @@ void Executor::doBuild()
addExecutorJobs();
- bool sourceFilesChanged = false;
- prepareAllArtifacts(&sourceFilesChanged);
+ prepareAllNodes();
prepareProducts();
setupRootNodes();
- prepareReachableArtifacts();
- setupProgressObserver(sourceFilesChanged);
- if (sourceFilesChanged)
- runAutoMoc();
+ prepareReachableNodes();
+ setupProgressObserver();
initLeaves();
if (!scheduleJobs()) {
m_logger.qbsTrace() << "Nothing to do at all, finishing.";
@@ -266,12 +226,12 @@ 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 == BuildGraphNode::Untouched)
return;
- artifact->buildState = Artifact::Buildable;
- foreach (Artifact *parent, artifact->parents)
+ node->buildState = BuildGraphNode::Buildable;
+ foreach (BuildGraphNode *parent, node->parents)
initArtifactsBottomUp(parent);
}
@@ -295,36 +255,37 @@ void Executor::initLeaves()
changedArtifacts.end());
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 == BuildGraphNode::Untouched) {
+ node->buildState = BuildGraphNode::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);
}
}
@@ -333,9 +294,30 @@ 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 */);
+ break;
+ case BuildGraphNode::Buildable:
+ // This is the only state in which we want to build a node.
+ nodeToBuild->accept(this);
+ break;
+ case BuildGraphNode::Building:
+ if (m_doDebug) {
+ m_logger.qbsDebug() << "[EXEC] " << nodeToBuild->toString();
+ m_logger.qbsDebug() << "[EXEC] node is currently being built. Skipping.";
+ }
+ break;
+ case BuildGraphNode::Built:
+ if (m_doDebug) {
+ m_logger.qbsDebug() << "[EXEC] " << nodeToBuild->toString();
+ m_logger.qbsDebug() << "[EXEC] node already built. Skipping.";
+ }
+ break;
+ }
}
return !m_leaves.empty() || !m_processingJobs.isEmpty();
}
@@ -363,11 +345,12 @@ bool Executor::isUpToDate(Artifact *artifact) const
return false;
}
- foreach (Artifact *child, artifact->children) {
- QBS_CHECK(child->timestamp().isValid());
+ foreach (Artifact *childArtifact, ArtifactSet::fromNodeSet(artifact->children)) {
+ QBS_CHECK(childArtifact->timestamp().isValid());
if (m_doDebug)
- 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;
}
@@ -416,13 +399,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
@@ -441,6 +417,11 @@ void Executor::buildArtifact(Artifact *artifact)
// Every generated artifact must have a transformer.
QBS_CHECK(artifact->transformer);
+ bool childrenAddedDueToRescue;
+ rescueOldBuildData(artifact, &childrenAddedDueToRescue);
+ if (childrenAddedDueToRescue && checkForUnbuiltDependencies(artifact))
+ return;
+
// Skip if outputs of this transformer are already built.
// That means we already ran the transformation.
foreach (Artifact *sideBySideArtifact, artifact->transformer->outputs) {
@@ -448,18 +429,18 @@ void Executor::buildArtifact(Artifact *artifact)
continue;
switch (sideBySideArtifact->buildState)
{
- case Artifact::Untouched:
- case Artifact::Buildable:
+ case BuildGraphNode::Untouched:
+ case BuildGraphNode::Buildable:
break;
- case Artifact::Built:
+ case BuildGraphNode::Built:
if (m_doDebug)
m_logger.qbsDebug() << "[EXEC] Side by side artifact already finished. Skipping.";
finishArtifact(artifact);
return;
- case Artifact::Building:
+ case BuildGraphNode::Building:
if (m_doDebug)
m_logger.qbsDebug() << "[EXEC] Side by side artifact processing. Skipping.";
- artifact->buildState = Artifact::Building;
+ artifact->buildState = BuildGraphNode::Building;
return;
}
}
@@ -496,36 +477,11 @@ void Executor::buildArtifact(Artifact *artifact)
scanner.scan();
// postpone the build of this artifact, if new dependencies found
- if (scanner.newDependencyAdded()) {
- bool buildingDependenciesFound = false;
- QVector<Artifact *> unbuiltDependencies;
- foreach (Artifact *dependency, artifact->children) {
- switch (dependency->buildState) {
- case Artifact::Untouched:
- case Artifact::Buildable:
- unbuiltDependencies += dependency;
- break;
- case Artifact::Building:
- buildingDependenciesFound = true;
- break;
- case Artifact::Built:
- // do nothing
- break;
- }
- }
- if (!unbuiltDependencies.isEmpty()) {
- artifact->inputsScanned = false;
- insertLeavesAfterAddingDependencies(unbuiltDependencies);
- return;
- }
- if (buildingDependenciesFound) {
- artifact->inputsScanned = false;
- return;
- }
- }
+ if (scanner.newDependencyAdded() && checkForUnbuiltDependencies(artifact))
+ return;
ExecutorJob *job = m_availableJobs.takeFirst();
- artifact->buildState = Artifact::Building;
+ artifact->buildState = BuildGraphNode::Building;
m_processingJobs.insert(job, artifact);
Q_ASSERT_X(artifact->product, Q_FUNC_INFO,
@@ -535,6 +491,69 @@ void Executor::buildArtifact(Artifact *artifact)
job->run(artifact->transformer.data(), artifact->product);
}
+void Executor::executeRuleNode(RuleNode *ruleNode)
+{
+ ArtifactSet changedInputArtifacts;
+ if (ruleNode->rule()->isDynamic()) {
+ foreach (Artifact *artifact, m_changedSourceArtifacts) {
+ if (artifact->product != ruleNode->product)
+ continue;
+ if (ruleNode->rule()->acceptsAsInput(artifact))
+ changedInputArtifacts += artifact;
+ }
+ foreach (Artifact *artifact,
+ ArtifactSet::fromNodeSet(ruleNode->product->buildData->nodes)) {
+ if (artifact->artifactType == Artifact::SourceFile)
+ continue;
+ if (artifact->timestampRetrieved && !isUpToDate(artifact)
+ && ruleNode->rule()->acceptsAsInput(artifact)) {
+ changedInputArtifacts += artifact;
+ }
+ }
+ }
+
+ RuleNode::ApplicationResult result;
+ ruleNode->apply(m_logger, changedInputArtifacts, &result);
+
+ if (result.upToDate) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] " << ruleNode->toString()
+ << " is up to date. Skipping.";
+ } else {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] " << ruleNode->toString();
+ const QVector<BuildGraphNode *> &createdNodes = result.createdNodes;
+ const WeakPointer<ResolvedProduct> &product = ruleNode->product;
+ QSet<RuleNode *> parentRules;
+ if (!createdNodes.isEmpty()) {
+ foreach (BuildGraphNode *parent, ruleNode->parents) {
+ if (RuleNode *parentRule = dynamic_cast<RuleNode *>(parent))
+ parentRules += parentRule;
+ }
+ }
+ foreach (BuildGraphNode *node, createdNodes) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] rule created " << node->toString();
+ loggedConnect(node, ruleNode, m_logger);
+ Artifact *outputArtifact = dynamic_cast<Artifact *>(node);
+ if (!outputArtifact)
+ continue;
+ if (outputArtifact->fileTags.matches(product->fileTags))
+ product->buildData->roots += outputArtifact;
+
+ foreach (Artifact *inputArtifact, outputArtifact->transformer->inputs)
+ loggedConnect(ruleNode, inputArtifact, m_logger);
+
+ foreach (RuleNode *parentRule, parentRules)
+ loggedConnect(parentRule, outputArtifact, m_logger);
+ }
+ insertLeavesAfterAddingDependencies(createdNodes);
+ }
+ finishNode(ruleNode);
+ if (m_progressObserver)
+ m_progressObserver->incrementProgressValue();
+}
+
void Executor::finishJob(ExecutorJob *job, bool success)
{
QBS_CHECK(job);
@@ -570,27 +589,21 @@ void Executor::finishJob(ExecutorJob *job, bool success)
}
}
-static bool allChildrenBuilt(Artifact *artifact)
+static bool allChildrenBuilt(BuildGraphNode *node)
{
- foreach (Artifact *child, artifact->children)
- if (child->buildState != Artifact::Built)
+ foreach (BuildGraphNode *child, node->children)
+ if (child->buildState != BuildGraphNode::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) {
- if (parent->buildState != Artifact::Buildable) {
+ leaf->buildState = BuildGraphNode::Built;
+ foreach (BuildGraphNode *parent, leaf->parents) {
+ if (parent->buildState != BuildGraphNode::Buildable) {
if (m_doTrace) {
- m_logger.qbsTrace() << "[EXEC] parent " << relativeArtifactFileName(parent)
+ m_logger.qbsTrace() << "[EXEC] parent " << parent->toString()
<< " build state: " << toString(parent->buildState);
}
continue;
@@ -599,48 +612,58 @@ 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);
- if (leaf->transformer)
+ finishNode(leaf);
+ m_scanResultCache.remove(leaf->filePath());
+
+ if (leaf->transformer) {
foreach (Artifact *sideBySideArtifact, leaf->transformer->outputs)
- if (leaf != sideBySideArtifact && sideBySideArtifact->buildState == Artifact::Building)
+ if (leaf != sideBySideArtifact
+ && sideBySideArtifact->buildState == BuildGraphNode::Building) {
finishArtifact(sideBySideArtifact);
-
- if (m_progressObserver && leaf->artifactType == Artifact::Generated)
- 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 == BuildGraphNode::Untouched)
+ node->buildState = BuildGraphNode::Buildable;
bool isLeaf = true;
- foreach (Artifact *child, artifact->children) {
- if (child->buildState != Artifact::Built) {
+ foreach (BuildGraphNode *child, node->children) {
+ if (child->buildState != BuildGraphNode::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);
}
}
@@ -649,11 +672,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()
@@ -665,20 +688,19 @@ void Executor::cancelJobs()
job->cancel();
}
-void Executor::setupProgressObserver(bool mocWillRun)
+void Executor::setupProgressObserver()
{
if (!m_progressObserver)
return;
- MocEffortCalculator mocEffortCalculator;
- BuildEffortCalculator buildEffortCalculator;
- foreach (const ResolvedProductConstPtr &product, m_productsToBuild)
- buildEffortCalculator.visitProduct(product);
- if (mocWillRun) {
- foreach (const ResolvedProductConstPtr &product, m_productsToBuild)
- mocEffortCalculator.visitProduct(product);
- }
- m_mocEffort = mocEffortCalculator.effort();
- const int totalEffort = m_mocEffort + buildEffortCalculator.effort();
+ int totalEffort = 1; // For the effort after the last rule application;
+ foreach (const ResolvedProductConstPtr &product, m_productsToBuild) {
+ QBS_CHECK(product->buildData);
+ foreach (const BuildGraphNode * const node, product->buildData->nodes) {
+ const RuleNode * const ruleNode = dynamic_cast<const RuleNode *>(node);
+ if (ruleNode)
+ ++totalEffort;
+ }
+ }
m_progressObserver->initialize(tr("Building%1").arg(configString()), totalEffort);
}
@@ -721,27 +743,89 @@ void Executor::addExecutorJobs()
}
}
-void Executor::runAutoMoc()
+void Executor::rescueOldBuildData(Artifact *artifact, bool *childrenAdded = 0)
{
- 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 == QLatin1String("Qt/core")) {
- autoMocApplied = true;
- m_autoMoc->apply(product);
- break;
+ if (childrenAdded)
+ *childrenAdded = false;
+ if (!artifact->oldDataPossiblyPresent)
+ return;
+ artifact->oldDataPossiblyPresent = false;
+ if (artifact->artifactType != Artifact::Generated)
+ return;
+
+ ResolvedProduct * const product = artifact->product.data();
+ AllRescuableArtifactData::Iterator it
+ = product->buildData->rescuableArtifactData.find(artifact->filePath());
+ if (it == product->buildData->rescuableArtifactData.end())
+ return;
+
+ const RescuableArtifactData &rad = it.value();
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] Attempting to rescue data of "
+ "artifact '%1'").arg(artifact->fileName());
+ }
+ if (commandListsAreEqual(artifact->transformer->commands, rad.commands)) {
+ artifact->setTimestamp(rad.timeStamp);
+ ResolvedProductPtr pseudoProduct = ResolvedProduct::create();
+ foreach (const RescuableArtifactData::ChildData &cd, rad.children) {
+ pseudoProduct->name = cd.productName;
+ Artifact * const child = lookupArtifact(pseudoProduct, m_project->buildData.data(),
+ cd.childFilePath, true);
+ if (!child || artifact->children.contains(child))
+ continue;
+ if (childrenAdded)
+ *childrenAdded = true;
+ safeConnect(artifact, child, m_logger);
+ if (cd.addedByScanner)
+ artifact->childrenAddedByScanner << child;
+ }
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "Data was rescued.";
+ } else {
+ removeGeneratedArtifactFromDisk(artifact->filePath(), m_logger);
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "Transformer commands changed, data not rescued.";
+ }
+ product->buildData->rescuableArtifactData.erase(it);
+}
+
+bool Executor::checkForUnbuiltDependencies(Artifact *artifact)
+{
+ bool buildingDependenciesFound = false;
+ QVector<BuildGraphNode *> unbuiltDependencies;
+ foreach (BuildGraphNode *dependency, artifact->children) {
+ switch (dependency->buildState) {
+ case BuildGraphNode::Untouched:
+ case BuildGraphNode::Buildable:
+ if (m_logger.debugEnabled()) {
+ m_logger.qbsDebug() << "[EXEC] unbuilt dependency: "
+ << dependency->toString();
+ }
+ unbuiltDependencies += dependency;
+ break;
+ case BuildGraphNode::Building: {
+ if (m_logger.debugEnabled()) {
+ m_logger.qbsDebug() << "[EXEC] dependency in state 'Building': "
+ << dependency->toString();
}
+ buildingDependenciesFound = true;
+ break;
+ }
+ case BuildGraphNode::Built:
+ // do nothing
+ break;
}
}
- if (autoMocApplied) {
- foreach (const ResolvedProductConstPtr &product, m_productsToBuild)
- CycleDetector(m_logger).visitProduct(product);
+ if (!unbuiltDependencies.isEmpty()) {
+ artifact->inputsScanned = false;
+ insertLeavesAfterAddingDependencies(unbuiltDependencies);
+ return true;
}
- if (m_progressObserver)
- m_progressObserver->incrementProgressValue(m_mocEffort);
+ if (buildingDependenciesFound) {
+ artifact->inputsScanned = false;
+ return true;
+ }
+ return false;
}
void Executor::onProcessError(const qbs::ErrorInfo &err)
@@ -790,12 +874,23 @@ 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 != BuildGraphNode::Built) {
unbuiltProductNames += product->name;
break;
}
}
+ if (!unbuiltProductNames.contains(product->name)) {
+ // Any element still left after a successful build has not been re-created
+ // by any rule and therefore does not exist anymore as an artifact.
+ foreach (const QString &filePath, product->buildData->rescuableArtifactData.keys())
+ removeGeneratedArtifactFromDisk(filePath, m_logger);
+ product->buildData->rescuableArtifactData.clear();
+
+ // Similar logic applies for the artifacts scheduled for potential rule application.
+ product->buildData->addedArtifactsByFileTag.clear();
+ product->buildData->removedArtifactsByFileTag.clear();
+ }
}
if (unbuiltProductNames.isEmpty()) {
@@ -813,58 +908,76 @@ void Executor::finish()
emit finished();
}
+bool Executor::visit(Artifact *artifact)
+{
+ buildArtifact(artifact);
+ 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;
-
- if (artifact->artifactType == Artifact::SourceFile) {
- const FileTime oldTimestamp = artifact->timestamp();
- retrieveSourceFileTimestamp(artifact);
- if (oldTimestamp != artifact->timestamp())
- *sourceFilesChanged = true;
- }
-
- // Timestamps of file dependencies must be invalid for every build.
- foreach (FileDependency *fileDependency, artifact->fileDependencies)
- fileDependency->clearTimestamp();
+ foreach (BuildGraphNode *node, product->buildData->nodes) {
+ node->buildState = BuildGraphNode::Untouched;
+ Artifact *artifact = dynamic_cast<Artifact *>(node);
+ if (artifact)
+ prepareArtifact(artifact);
}
}
}
+void Executor::prepareArtifact(Artifact *artifact)
+{
+ artifact->inputsScanned = false;
+ artifact->timestampRetrieved = false;
+
+ 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();
+}
+
/**
* Walk the build graph top-down from the roots and for each reachable node N
* - mark N as buildable.
*/
-void Executor::prepareReachableArtifacts()
+void Executor::prepareReachableNodes()
{
- const Artifact::BuildState initialBuildState = m_buildOptions.changedFiles().isEmpty()
- ? Artifact::Buildable : Artifact::Built;
- foreach (Artifact *root, m_roots)
- prepareReachableArtifacts_impl(root, initialBuildState);
+ const BuildGraphNode::BuildState initialBuildState = m_buildOptions.changedFiles().isEmpty()
+ ? BuildGraphNode::Buildable : BuildGraphNode::Built;
+ foreach (BuildGraphNode *root, m_roots)
+ prepareReachableNodes_impl(root, initialBuildState);
}
-void Executor::prepareReachableArtifacts_impl(Artifact *artifact,
- const Artifact::BuildState buildState)
+void Executor::prepareReachableNodes_impl(BuildGraphNode *node,
+ const BuildGraphNode::BuildState buildState)
{
- if (artifact->buildState != Artifact::Untouched)
+ if (node->buildState != BuildGraphNode::Untouched)
return;
- artifact->buildState = buildState;
- foreach (Artifact *child, artifact->children)
- prepareReachableArtifacts_impl(child, buildState);
+ node->buildState = buildState;
+ foreach (BuildGraphNode *child, node->children)
+ prepareReachableNodes_impl(child, buildState);
}
void Executor::prepareProducts()
@@ -879,28 +992,29 @@ void Executor::setupRootNodes()
{
m_roots.clear();
foreach (const ResolvedProductPtr &product, m_productsToBuild) {
- foreach (Artifact *targetArtifact, product->buildData->targetArtifacts)
- m_roots += targetArtifact;
+ foreach (BuildGraphNode *root, product->buildData->roots)
+ m_roots += root;
}
}
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/corelib/buildgraph/executor.h b/src/lib/corelib/buildgraph/executor.h
index 446ed8c81..e402e303b 100644
--- a/src/lib/corelib/buildgraph/executor.h
+++ b/src/lib/corelib/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,13 @@ namespace qbs {
class ProcessResult;
namespace Internal {
-class AutoMoc;
class ExecutorJob;
class FileTime;
class InputArtifactScannerContext;
class ProgressObserver;
+class RuleNode;
-class Executor : public QObject
+class Executor : public QObject, private BuildGraphVisitor
{
Q_OBJECT
@@ -82,44 +83,52 @@ private slots:
void finish();
private:
+ // BuildGraphVisitor implementation
+ bool visit(Artifact *artifact);
+ 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 prepareReachableArtifacts();
- void prepareReachableArtifacts_impl(Artifact *artifact, const Artifact::BuildState buildState);
+ void prepareAllNodes();
+ void prepareArtifact(Artifact *artifact);
+ void prepareReachableNodes();
+ void prepareReachableNodes_impl(BuildGraphNode *node, const Artifact::BuildState buildState);
void prepareProducts();
void setupRootNodes();
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();
- void initLeavesTopDown(Artifact *artifact, QSet<Artifact *> &seenArtifacts);
+ void initLeavesTopDown(BuildGraphNode *artifact, QSet<BuildGraphNode *> &seenArtifacts);
bool scheduleJobs();
void buildArtifact(Artifact *artifact);
+ void executeRuleNode(RuleNode *ruleNode);
void finishJob(ExecutorJob *job, bool success);
+ void finishNode(BuildGraphNode *leaf);
void finishArtifact(Artifact *artifact);
void setState(ExecutorState);
void addExecutorJobs();
- void runAutoMoc();
- void insertLeavesAfterAddingDependencies(QVector<Artifact *> dependencies);
+ void insertLeavesAfterAddingDependencies(QVector<BuildGraphNode *> dependencies);
void cancelJobs();
- void setupProgressObserver(bool mocWillRun);
+ void setupProgressObserver();
void doSanityChecks();
void handleError(const ErrorInfo &error);
+ void rescueOldBuildData(Artifact *artifact, bool *childrenAdded);
+ bool checkForUnbuiltDependencies(Artifact *artifact);
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;
@@ -131,12 +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;
FileTags m_activeFileTags;
diff --git a/src/lib/corelib/buildgraph/executorjob.cpp b/src/lib/corelib/buildgraph/executorjob.cpp
index 3ad3df6d5..281d82e36 100644
--- a/src/lib/corelib/buildgraph/executorjob.cpp
+++ b/src/lib/corelib/buildgraph/executorjob.cpp
@@ -117,7 +117,7 @@ void ExecutorJob::runNextCommand()
return;
}
- const AbstractCommand * const command = m_transformer->commands.at(m_currentCommandIdx);
+ const AbstractCommandPtr &command = m_transformer->commands.at(m_currentCommandIdx);
switch (command->type()) {
case AbstractCommand::ProcessCommandType:
m_currentCommandExecutor = m_processCommandExecutor;
@@ -129,7 +129,7 @@ void ExecutorJob::runNextCommand()
qFatal("Missing implementation for command type %d", command->type());
}
- m_currentCommandExecutor->start(m_transformer, command);
+ m_currentCommandExecutor->start(m_transformer, command.data());
}
void ExecutorJob::onCommandError(const ErrorInfo &err)
diff --git a/src/lib/corelib/buildgraph/forward_decls.h b/src/lib/corelib/buildgraph/forward_decls.h
index 7f3986a9b..2baa74d6d 100644
--- a/src/lib/corelib/buildgraph/forward_decls.h
+++ b/src/lib/corelib/buildgraph/forward_decls.h
@@ -45,6 +45,15 @@ typedef QSharedPointer<const Transformer> TransformerConstPtr;
class RulesEvaluationContext;
typedef QSharedPointer<RulesEvaluationContext> RulesEvaluationContextPtr;
+class AbstractCommand;
+typedef QSharedPointer<AbstractCommand> AbstractCommandPtr;
+
+class ProcessCommand;
+typedef QSharedPointer<ProcessCommand> ProcessCommandPtr;
+
+class JavaScriptCommand;
+typedef QSharedPointer<JavaScriptCommand> JavaScriptCommandPtr;
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/inputartifactscanner.cpp b/src/lib/corelib/buildgraph/inputartifactscanner.cpp
index d1bae81dd..36defdcb0 100644
--- a/src/lib/corelib/buildgraph/inputartifactscanner.cpp
+++ b/src/lib/corelib/buildgraph/inputartifactscanner.cpp
@@ -301,7 +301,7 @@ resolved:
// file or conflict with the writing process.
if (filePathsToScan) {
Artifact *artifactDependency = dynamic_cast<Artifact *>(resolvedDependency->file);
- if (!artifactDependency || artifactDependency->buildState != Artifact::Building)
+ if (!artifactDependency || artifactDependency->buildState != BuildGraphNode::Building)
filePathsToScan->append(resolvedDependency->filePath);
}
handleDependency(*resolvedDependency);
@@ -360,7 +360,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/corelib/buildgraph/jscommandexecutor.cpp b/src/lib/corelib/buildgraph/jscommandexecutor.cpp
index b51dd6475..2c1973d90 100644
--- a/src/lib/corelib/buildgraph/jscommandexecutor.cpp
+++ b/src/lib/corelib/buildgraph/jscommandexecutor.cpp
@@ -84,7 +84,7 @@ public slots:
setupScriptEngineForFile(scriptEngine, transformer->rule->prepareScript->fileContext, scope);
setupScriptEngineForProduct(scriptEngine, transformer->product(), transformer->rule, scope,
&observer);
- transformer->setupInputs(scriptEngine, scope);
+ transformer->setupInputs(scope);
transformer->setupOutputs(scriptEngine, scope);
for (QVariantMap::const_iterator it = cmd->properties().constBegin();
diff --git a/src/lib/corelib/buildgraph/nodeset.cpp b/src/lib/corelib/buildgraph/nodeset.cpp
new file mode 100644
index 000000000..176cab53c
--- /dev/null
+++ b/src/lib/corelib/buildgraph/nodeset.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 "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)
+{
+ m_data.insert(other.begin(), other.end());
+ return *this;
+}
+
+void NodeSet::remove(BuildGraphNode *node)
+{
+ m_data.erase(node);
+}
+
+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::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/corelib/buildgraph/nodeset.h b/src/lib/corelib/buildgraph/nodeset.h
new file mode 100644
index 000000000..9bcf780ce
--- /dev/null
+++ b/src/lib/corelib/buildgraph/nodeset.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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_NODESET_H
+#define QBS_NODESET_H
+
+#include <set>
+#include <cstddef>
+
+namespace qbs {
+namespace Internal {
+
+class BuildGraphNode;
+class PersistentPool;
+
+/**
+ * Set of build graph nodes.
+ * This is faster than QSet when iterating over the container.
+ */
+class NodeSet
+{
+public:
+ NodeSet();
+ NodeSet(const NodeSet &other);
+
+ NodeSet &unite(const NodeSet &other);
+
+ 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(); }
+ const_iterator begin() const { return m_data.begin(); }
+ const_iterator end() const { return m_data.end(); }
+ const_iterator constBegin() const { return m_data.begin(); }
+ const_iterator constEnd() const { return m_data.end(); }
+
+ void insert(BuildGraphNode *node)
+ {
+ m_data.insert(node);
+ }
+
+ void operator+=(BuildGraphNode *node)
+ {
+ insert(node);
+ }
+
+ NodeSet &operator<<(BuildGraphNode *node)
+ {
+ insert(node);
+ return *this;
+ }
+
+ void remove(BuildGraphNode *node);
+
+ bool contains(BuildGraphNode *node) const
+ {
+ return m_data.find(node) != m_data.end();
+ }
+
+ void clear()
+ {
+ m_data.clear();
+ }
+
+ bool isEmpty() const
+ {
+ return m_data.empty();
+ }
+
+ int count() const
+ {
+ return (int)m_data.size();
+ }
+
+ void reserve(int)
+ {
+ // no-op
+ }
+
+ 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<BuildGraphNode *> m_data;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_NODESET_H
diff --git a/src/lib/corelib/buildgraph/productbuilddata.cpp b/src/lib/corelib/buildgraph/productbuilddata.cpp
index 0b9f36ab1..cdd3874e7 100644
--- a/src/lib/corelib/buildgraph/productbuilddata.cpp
+++ b/src/lib/corelib/buildgraph/productbuilddata.cpp
@@ -29,6 +29,7 @@
#include "productbuilddata.h"
#include "artifact.h"
+#include "command.h"
#include "projectbuilddata.h"
#include <language/language.h>
#include <logging/logger.h>
@@ -41,19 +42,74 @@ namespace Internal {
ProductBuildData::~ProductBuildData()
{
- qDeleteAll(artifacts);
+ qDeleteAll(nodes);
+}
+
+ArtifactSet ProductBuildData::targetArtifacts() const
+{
+ return ArtifactSet::fromNodeSet(roots);
+}
+
+static void loadArtifactSetByFileTag(PersistentPool &pool,
+ ProductBuildData::ArtifactSetByFileTag &s)
+{
+ int elemCount;
+ pool.stream() >> elemCount;
+ for (int i = 0; i < elemCount; ++i) {
+ QVariant fileTag;
+ pool.stream() >> fileTag;
+ ArtifactSet artifacts;
+ pool.loadContainer(artifacts);
+ s.insert(FileTag::fromSetting(fileTag), artifacts);
+ }
}
void ProductBuildData::load(PersistentPool &pool)
{
- pool.loadContainer(artifacts);
- pool.loadContainer(targetArtifacts);
+ nodes.load(pool);
+ roots.load(pool);
+ int rescuableArtifactCount;
+ pool.stream() >> rescuableArtifactCount;
+ rescuableArtifactData.reserve(rescuableArtifactCount);
+ for (int i = 0; i < rescuableArtifactCount; ++i) {
+ const QString filePath = pool.idLoadString();
+ RescuableArtifactData elem;
+ elem.load(pool);
+ rescuableArtifactData.insert(filePath, elem);
+ }
+ loadArtifactSetByFileTag(pool, addedArtifactsByFileTag);
+ loadArtifactSetByFileTag(pool, removedArtifactsByFileTag);
+}
+
+static void storeArtifactSetByFileTag(PersistentPool &pool,
+ const ProductBuildData::ArtifactSetByFileTag &s)
+{
+ pool.stream() << s.count();
+ ProductBuildData::ArtifactSetByFileTag::ConstIterator it;
+ for (it = s.constBegin(); it != s.constEnd(); ++it) {
+ pool.stream() << it.key().toSetting();
+ pool.storeContainer(it.value());
+ }
}
void ProductBuildData::store(PersistentPool &pool) const
{
- pool.storeContainer(artifacts);
- pool.storeContainer(targetArtifacts);
+ nodes.store(pool);
+ roots.store(pool);
+ pool.stream() << rescuableArtifactData.count();
+ for (AllRescuableArtifactData::ConstIterator it = rescuableArtifactData.constBegin();
+ it != rescuableArtifactData.constEnd(); ++it) {
+ pool.storeString(it.key());
+ it.value().store(pool);
+ }
+ storeArtifactSetByFileTag(pool, addedArtifactsByFileTag);
+ storeArtifactSetByFileTag(pool, removedArtifactsByFileTag);
+}
+
+void addArtifactToSet(Artifact *artifact, ProductBuildData::ArtifactSetByFileTag &container)
+{
+ foreach (const FileTag &tag, artifact->fileTags)
+ container[tag] += artifact;
}
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/productbuilddata.h b/src/lib/corelib/buildgraph/productbuilddata.h
index 1e4b4402e..10b4655be 100644
--- a/src/lib/corelib/buildgraph/productbuilddata.h
+++ b/src/lib/corelib/buildgraph/productbuilddata.h
@@ -30,8 +30,10 @@
#define QBS_PRODUCTBUILDDATA_H
#include "artifactset.h"
+#include "nodeset.h"
+#include "rescuableartifactdata.h"
+#include <language/filetags.h>
#include <language/forward_decls.h>
-
#include <tools/persistentobject.h>
#include <QList>
@@ -39,6 +41,7 @@
namespace qbs {
namespace Internal {
+
class Logger;
class ProductBuildData : public PersistentObject
@@ -46,17 +49,33 @@ class ProductBuildData : public PersistentObject
public:
~ProductBuildData();
- QSet<Artifact *> targetArtifacts;
- ArtifactSet artifacts;
- QList<RuleConstPtr> topSortedRules;
+ ArtifactSet targetArtifacts() const;
+ NodeSet nodes;
+ NodeSet roots;
+
+ // After change tracking, this is the relevant data of artifacts that were in the build data
+ // of the restored product, and will potentially be re-created by our rules.
+ // If and when that happens, the relevant data will be copied over to the newly created
+ // artifact.
+ AllRescuableArtifactData rescuableArtifactData;
// Do not store, initialized in executor. Higher prioritized artifacts are built first.
unsigned int buildPriority;
+ typedef QHash<FileTag, ArtifactSet> ArtifactSetByFileTag;
+ ArtifactSetByFileTag addedArtifactsByFileTag;
+ ArtifactSetByFileTag removedArtifactsByFileTag;
+
+ // TODO: Serialize.
+ typedef QHash<RuleConstPtr, ArtifactSet> ArtifactSetByRule;
+ ArtifactSetByRule artifactsWithChangedInputsPerRule;
+
void load(PersistentPool &pool);
void store(PersistentPool &pool) const;
};
+void addArtifactToSet(Artifact *artifact, ProductBuildData::ArtifactSetByFileTag &container);
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/productinstaller.cpp b/src/lib/corelib/buildgraph/productinstaller.cpp
index 867ad1370..c7df23d24 100644
--- a/src/lib/corelib/buildgraph/productinstaller.cpp
+++ b/src/lib/corelib/buildgraph/productinstaller.cpp
@@ -82,7 +82,7 @@ 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 Artifact *artifact, ArtifactSet::fromNodeSet(product->buildData->nodes)) {
if (artifact->properties->qbsPropertyValue(QLatin1String("install")).toBool())
artifactsToInstall += artifact;
}
diff --git a/src/lib/corelib/buildgraph/projectbuilddata.cpp b/src/lib/corelib/buildgraph/projectbuilddata.cpp
index 8e3bfd3e0..96d0e925c 100644
--- a/src/lib/corelib/buildgraph/projectbuilddata.cpp
+++ b/src/lib/corelib/buildgraph/projectbuilddata.cpp
@@ -30,8 +30,11 @@
#include "artifact.h"
#include "buildgraph.h"
+#include "buildgraphvisitor.h"
#include "productbuilddata.h"
#include "command.h"
+#include "rulegraph.h"
+#include "rulenode.h"
#include "rulesapplicator.h"
#include "rulesevaluationcontext.h"
#include "transformer.h"
@@ -111,36 +114,36 @@ 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();
}
-static void disconnectArtifactParents(Artifact *artifact, ProjectBuildData *projectBuildData,
- const Logger &logger)
+static void disconnectArtifactParents(Artifact *artifact, const Logger &logger)
{
if (logger.traceEnabled()) {
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) {
+ QBS_CHECK(parentArtifact->transformer);
+ parentArtifact->childrenAddedByScanner.remove(artifact);
+ parentArtifact->transformer->inputs.remove(artifact);
+ parentArtifact->product->registerArtifactWithChangedInputs(parentArtifact);
}
}
artifact->parents.clear();
}
-static void disconnectArtifact(Artifact *artifact, ProjectBuildData *projectBuildData,
- const Logger &logger)
+static void disconnectArtifact(Artifact *artifact, const Logger &logger)
{
disconnectArtifactChildren(artifact, logger);
- disconnectArtifactParents(artifact, projectBuildData, logger);
+ disconnectArtifactParents(artifact, logger);
}
/*!
@@ -154,13 +157,14 @@ void ProjectBuildData::removeArtifactAndExclusiveDependents(Artifact *artifact,
{
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()) {
removeParent = true;
} else if (parent->transformer) {
- artifactsThatMustGetNewTransformers += parent;
+ parent->product->registerArtifactWithChangedInputs(parent);
parent->transformer->inputs.remove(artifact);
removeParent = parent->transformer->inputs.isEmpty();
}
@@ -171,6 +175,7 @@ void ProjectBuildData::removeArtifactAndExclusiveDependents(Artifact *artifact,
}
const bool removeFromDisk = artifact->artifactType == Artifact::Generated;
removeArtifact(artifact, logger, removeFromDisk, removeFromProduct);
+
}
void ProjectBuildData::removeArtifact(Artifact *artifact,
@@ -183,49 +188,18 @@ void ProjectBuildData::removeArtifact(Artifact *artifact,
removeGeneratedArtifactFromDisk(artifact, logger);
removeFromLookupTable(artifact);
if (removeFromProduct) {
- artifact->product->buildData->artifacts.remove(artifact);
- artifact->product->buildData->targetArtifacts.remove(artifact);
+ artifact->product->buildData->nodes.remove(artifact);
+ artifact->product->buildData->roots.remove(artifact);
}
- disconnectArtifact(artifact, this, logger);
- artifactsThatMustGetNewTransformers -= artifact;
- isDirty = true;
-}
-void ProjectBuildData::updateNodesThatMustGetNewTransformer(const Logger &logger)
-{
- RulesEvaluationContext::Scope s(evaluationContext.data());
- foreach (Artifact *artifact, artifactsThatMustGetNewTransformers)
- updateNodeThatMustGetNewTransformer(artifact, logger);
- artifactsThatMustGetNewTransformers.clear();
-}
-
-void ProjectBuildData::updateNodeThatMustGetNewTransformer(Artifact *artifact, const Logger &logger)
-{
- QBS_CHECK(artifact->transformer);
+ // If removal is requested and the executor has not run since the time the artifact was last
+ // added, we must undo the "register" operation.
+ artifact->product->unregisterAddedArtifact(artifact);
- if (logger.debugEnabled()) {
- logger.qbsDebug() << "[BG] updating transformer for "
- << relativeArtifactFileName(artifact);
- }
-
- removeGeneratedArtifactFromDisk(artifact, logger);
- artifact->autoMocTimestamp.clear();
- artifact->clearTimestamp();
-
- const RuleConstPtr rule = artifact->transformer->rule;
+ disconnectArtifact(artifact, logger);
+ if (artifact->transformer)
+ artifact->product->unregisterArtifactWithChangedInputs(artifact);
isDirty = true;
-
- QBS_CHECK(artifact->transformer);
- foreach (Artifact * const sibling, artifact->transformer->outputs)
- sibling->transformer.clear();
-
- ArtifactsPerFileTagMap artifactsPerFileTag;
- foreach (Artifact *input, artifact->children) {
- foreach (const FileTag &fileTag, input->fileTags)
- artifactsPerFileTag[fileTag] += input;
- }
- RulesApplicator rulesApplier(artifact->product, artifactsPerFileTag, logger);
- rulesApplier.applyRule(rule);
}
void ProjectBuildData::load(PersistentPool &pool)
@@ -275,10 +249,172 @@ void BuildDataResolver::resolveProductBuildDataForExistingProject(const TopLevel
m_project = project;
foreach (const ResolvedProductPtr &product, freshProducts) {
if (product->enabled)
- resolveProductBuildData(product);
+ resolveProductBuildDataForExistingProject(product);
+ }
+}
+
+static QSet<ResolvedProductPtr> findDependentProducts(const ResolvedProductPtr &product)
+{
+ QSet<ResolvedProductPtr> result;
+ foreach (const ResolvedProductPtr &parent, product->topLevelProject()->allProducts()) {
+ if (parent->dependencies.contains(product))
+ result += parent;
+ }
+ return result;
+}
+
+class FindLeafRules : public BuildGraphVisitor
+{
+public:
+ FindLeafRules()
+ {
+ }
+
+ const QSet<RuleNode *> &apply(const ResolvedProductPtr &product)
+ {
+ m_result.clear();
+ m_product = product;
+ foreach (BuildGraphNode *n, product->buildData->nodes)
+ n->accept(this);
+ return m_result;
+ }
+
+private:
+ virtual bool visit(Artifact *)
+ {
+ return false;
+ }
+
+ virtual bool visit(RuleNode *node)
+ {
+ if (!hasChildRuleInThisProduct(node))
+ m_result << node;
+ return false;
+ }
+
+ bool hasChildRuleInThisProduct(const RuleNode *node) const
+ {
+ foreach (BuildGraphNode *c, node->children) {
+ if (c->product == m_product && c->type() == BuildGraphNode::RuleNodeType)
+ return true;
+ }
+ return false;
+ }
+
+ ResolvedProductPtr m_product;
+ QSet<RuleNode *> m_result;
+};
+
+class FindRootRules : public BuildGraphVisitor
+{
+public:
+ FindRootRules()
+ {
+ }
+
+ const QList<RuleNode *> &apply(const ResolvedProductPtr &product)
+ {
+ m_result.clear();
+ foreach (BuildGraphNode *n, product->buildData->roots)
+ n->accept(this);
+ return m_result;
+ }
+
+private:
+ virtual bool visit(Artifact *)
+ {
+ return true;
+ }
+
+ virtual bool visit(RuleNode *node)
+ {
+ m_result << node;
+ return true;
+ }
+
+ QList<RuleNode *> m_result;
+};
+
+
+void BuildDataResolver::resolveProductBuildDataForExistingProject(const ResolvedProductPtr &product)
+{
+ resolveProductBuildData(product);
+
+ // Connect the leaf rules of all dependent products to the root rules of this product.
+ const QList<RuleNode *> rootRules = FindRootRules().apply(product);
+ QSet<ResolvedProductPtr> dependents = findDependentProducts(product);
+ foreach (const ResolvedProductPtr &dependentProduct, dependents) {
+ foreach (RuleNode *leaf, FindLeafRules().apply(dependentProduct)) {
+ foreach (RuleNode *root, rootRules) {
+ loggedConnect(leaf, root, m_logger);
+ }
+ }
}
}
+class CreateRuleNodes : public RuleGraphVisitor
+{
+public:
+ CreateRuleNodes(const ResolvedProductPtr &product, const Logger &logger)
+ : m_product(product), m_logger(logger)
+ {
+ }
+
+ const QSet<RuleNode *> &leaves() const
+ {
+ return m_leaves;
+ }
+
+private:
+ const ResolvedProductPtr &m_product;
+ const Logger &m_logger;
+ QHash<RuleConstPtr, RuleNode *> m_nodePerRule;
+ QSet<const Rule *> m_rulesOnPath;
+ QList<const Rule *> m_rulePath;
+ QSet<RuleNode *> m_leaves;
+
+ void visit(const RuleConstPtr &parentRule, const RuleConstPtr &rule)
+ {
+ if (m_rulesOnPath.contains(rule.data())) {
+ QString pathstr;
+ foreach (const Rule *r, m_rulePath) {
+ pathstr += QLatin1Char('\n') + r->toString() + QLatin1Char('\t')
+ + r->prepareScript->location.toString();
+ }
+ throw ErrorInfo(Tr::tr("Cycle detected in rule dependencies: %1").arg(pathstr));
+ }
+ m_rulesOnPath.insert(rule.data());
+ m_rulePath.append(rule.data());
+ RuleNode *node = m_nodePerRule.value(rule);
+ if (!node) {
+ node = new RuleNode;
+ m_leaves.insert(node);
+ m_nodePerRule.insert(rule, node);
+ node->product = m_product;
+ node->setRule(rule);
+ m_product->buildData->nodes += node;
+ if (m_logger.debugEnabled()) {
+ m_logger.qbsDebug() << "[BG] create " << node->toString()
+ << " for product " << m_product->name;
+ }
+ }
+ if (parentRule) {
+ RuleNode *parent = m_nodePerRule.value(parentRule);
+ QBS_CHECK(parent);
+ loggedConnect(parent, node, m_logger);
+ m_leaves.remove(parent);
+ } else {
+ m_product->buildData->roots += node;
+ }
+ }
+
+ void endVisit(const RuleConstPtr &rule)
+ {
+ m_rulesOnPath.remove(rule.data());
+ m_rulePath.removeLast();
+ }
+};
+
void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &product)
{
if (product->buildData)
@@ -308,6 +444,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc
}
qbsFileArtifact->fileTags.insert("qbs");
artifactsPerFileTag["qbs"].insert(qbsFileArtifact);
+ product->registerAddedArtifact(qbsFileArtifact);
// read sources
foreach (const SourceArtifactConstPtr &sourceArtifact, product->allEnabledFiles()) {
@@ -316,6 +453,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc
continue; // ignore duplicate artifacts
Artifact *artifact = createArtifact(product, sourceArtifact, m_logger);
+ product->registerAddedArtifact(artifact);
foreach (const FileTag &fileTag, artifact->fileTags)
artifactsPerFileTag[fileTag].insert(artifact);
}
@@ -345,11 +483,12 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc
outputArtifact->artifactType = Artifact::Generated;
outputArtifact->transformer = transformer;
transformer->outputs += outputArtifact;
- product->buildData->targetArtifacts += outputArtifact;
+ product->buildData->roots += outputArtifact;
foreach (Artifact *inputArtifact, inputArtifacts)
safeConnect(outputArtifact, inputArtifact, m_logger);
foreach (const FileTag &fileTag, outputArtifact->fileTags)
artifactsPerFileTag[fileTag].insert(outputArtifact);
+ product->registerAddedArtifact(outputArtifact);
RuleArtifactPtr ruleArtifact = RuleArtifact::create();
ruleArtifact->fileName = outputArtifact->filePath();
@@ -364,7 +503,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc
PrepareScriptObserver observer(engine());
setupScriptEngineForProduct(engine(), product, transformer->rule, prepareScriptContext,
&observer);
- transformer->setupInputs(engine(), prepareScriptContext);
+ transformer->setupInputs(prepareScriptContext);
transformer->setupOutputs(engine(), prepareScriptContext);
transformer->createCommands(rtrafo->transform, evalContext(),
ScriptEngine::argumentList(transformer->rule->prepareScript->argumentNames,
@@ -387,8 +526,23 @@ 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);
+
+ // Connect the leaf rules of this product to the root rules of all product dependencies.
+ foreach (const ResolvedProductConstPtr &dep, product->dependencies) {
+ if (!dep->buildData)
+ continue;
+ foreach (BuildGraphNode *depRoot, dep->buildData->roots) {
+ RuleNode *depRootRule = dynamic_cast<RuleNode *>(depRoot);
+ if (!depRootRule)
+ continue;
+ foreach (RuleNode *leafRule, crn.leaves())
+ loggedConnect(leafRule, depRootRule, m_logger);
+ }
+ }
}
RulesEvaluationContextPtr BuildDataResolver::evalContext() const
diff --git a/src/lib/corelib/buildgraph/projectbuilddata.h b/src/lib/corelib/buildgraph/projectbuilddata.h
index efc756428..afbd363d5 100644
--- a/src/lib/corelib/buildgraph/projectbuilddata.h
+++ b/src/lib/corelib/buildgraph/projectbuilddata.h
@@ -29,7 +29,6 @@
#ifndef QBS_PROJECTBUILDDATA_H
#define QBS_PROJECTBUILDDATA_H
-#include "artifactset.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;
@@ -62,21 +63,21 @@ public:
QList<FileResourceBase *> lookupFiles(const QString &dirPath, const QString &fileName) const;
QList<FileResourceBase *> lookupFiles(const Artifact *artifact) const;
void insertFileDependency(FileDependency *dependency);
- void updateNodesThatMustGetNewTransformer(const Logger &logger);
void removeArtifactAndExclusiveDependents(Artifact *artifact, const Logger &logger,
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;
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;
@@ -95,6 +96,7 @@ public:
const QList<ResolvedProductPtr> &freshProducts);
private:
+ void resolveProductBuildDataForExistingProject(const ResolvedProductPtr &product);
void resolveProductBuildData(const ResolvedProductPtr &product);
RulesEvaluationContextPtr evalContext() const;
ScriptEngine *engine() const;
diff --git a/src/lib/corelib/buildgraph/qtmocscanner.cpp b/src/lib/corelib/buildgraph/qtmocscanner.cpp
new file mode 100644
index 000000000..995250441
--- /dev/null
+++ b/src/lib/corelib/buildgraph/qtmocscanner.cpp
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 "qtmocscanner.h"
+
+#include "artifact.h"
+#include "productbuilddata.h"
+#include "scanresultcache.h"
+#include <tools/qbsassert.h>
+#include <tools/scannerpluginmanager.h>
+#include <tools/scripttools.h>
+
+#include <QScriptContext>
+#include <QScriptEngine>
+#include <QDebug>
+
+namespace qbs {
+namespace Internal {
+
+QtMocScanner::QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue,
+ const Logger &logger)
+ : m_product(product)
+ , m_targetScriptValue(targetScriptValue)
+ , m_logger(logger)
+ , m_scanResultCache(new ScanResultCache)
+ , m_cppScanner(0)
+ , m_hppScanner(0)
+{
+ QScriptEngine *engine = targetScriptValue.engine();
+ QScriptValue scannerObj = engine->newObject();
+ targetScriptValue.setProperty(QLatin1String("QtMocScanner"), scannerObj);
+ QScriptValue applyFunction = engine->newFunction(&js_apply, this);
+ scannerObj.setProperty(QLatin1String("apply"), applyFunction);
+}
+
+QtMocScanner::~QtMocScanner()
+{
+ m_targetScriptValue.setProperty(QLatin1String("QtMocScanner"), QScriptValue());
+ delete m_scanResultCache;
+}
+
+static ScanResultCache::Result runScanner(ScannerPlugin *scanner, const Artifact *artifact,
+ ScanResultCache *scanResultCache)
+{
+ ScanResultCache::Result scanResult = scanResultCache->value(artifact->filePath());
+ if (!scanResult.valid) {
+ scanResult.valid = true;
+ void *opaq = scanner->open(artifact->filePath().utf16(),
+ ScanForDependenciesFlag | ScanForFileTagsFlag);
+ if (!opaq || !scanner->additionalFileTags)
+ return scanResult;
+
+ int length = 0;
+ const char **szFileTagsFromScanner = scanner->additionalFileTags(opaq, &length);
+ if (szFileTagsFromScanner) {
+ 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);
+ scanResultCache->insert(artifact->filePath(), scanResult);
+ }
+ return scanResult;
+}
+
+void QtMocScanner::findIncludedMocCppFiles()
+{
+ if (!m_includedMocCppFiles.isEmpty())
+ return;
+
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[QtMocScanner] looking for included moc_XXX.cpp files";
+
+ foreach (Artifact *artifact, m_product->lookupArtifactsByFileTag("cpp")) {
+ const ScanResultCache::Result scanResult
+ = runScanner(m_cppScanner, artifact, m_scanResultCache);
+ 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() << "[QtMocScanner] " << artifact->fileName()
+ << " includes " << includedFilePath;
+ includedFilePath.remove(0, 4);
+ includedFilePath.chop(4);
+ m_includedMocCppFiles.insert(includedFilePath, artifact->fileName());
+ }
+ }
+ }
+}
+
+QScriptValue QtMocScanner::js_apply(QScriptContext *ctx, QScriptEngine *engine, void *data)
+{
+ QtMocScanner *that = reinterpret_cast<QtMocScanner *>(data);
+ QScriptValue input = ctx->argument(0);
+ return that->apply(engine, attachedPointer<Artifact>(input));
+}
+
+QScriptValue QtMocScanner::apply(QScriptEngine *engine, const Artifact *artifact)
+{
+ if (!m_cppScanner) {
+ QList<ScannerPlugin *> scanners = ScannerPluginManager::scannersForFileTag("cpp");
+ QBS_CHECK(scanners.count() == 1);
+ m_cppScanner = scanners.first();
+ scanners = ScannerPluginManager::scannersForFileTag("hpp");
+ QBS_CHECK(scanners.count() == 1);
+ m_hppScanner = scanners.first();
+ }
+
+ findIncludedMocCppFiles();
+
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[QtMocScanner] scanning " << artifact->toString();
+
+ bool hasQObjectMacro = false;
+ bool mustCompile = false;
+ bool hasPluginMetaDataMacro = false;
+ const bool isHeaderFile = artifact->fileTags.contains("hpp");
+
+ ScannerPlugin * const scanner = isHeaderFile ? m_hppScanner : m_cppScanner;
+ const ScanResultCache::Result scanResult = runScanner(scanner, artifact, m_scanResultCache);
+ if (!scanResult.additionalFileTags.isEmpty()) {
+ if (isHeaderFile) {
+ if (scanResult.additionalFileTags.contains("moc_hpp"))
+ hasQObjectMacro = true;
+ if (scanResult.additionalFileTags.contains("moc_hpp_plugin")) {
+ hasQObjectMacro = true;
+ hasPluginMetaDataMacro = true;
+ }
+ if (!m_includedMocCppFiles.contains(FileInfo::completeBaseName(artifact->fileName())))
+ mustCompile = true;
+ } else {
+ if (scanResult.additionalFileTags.contains("moc_cpp"))
+ hasQObjectMacro = true;
+ }
+ }
+
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << "[QtMocScanner] hasQObjectMacro: " << hasQObjectMacro
+ << " mustCompile: " << mustCompile
+ << " hasPluginMetaDataMacro: " << hasPluginMetaDataMacro;
+ }
+
+ QScriptValue obj = engine->newObject();
+ obj.setProperty(QLatin1String("hasQObjectMacro"), hasQObjectMacro);
+ obj.setProperty(QLatin1String("mustCompile"), mustCompile);
+ obj.setProperty(QLatin1String("hasPluginMetaDataMacro"), hasPluginMetaDataMacro);
+ return obj;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/automoc.h b/src/lib/corelib/buildgraph/qtmocscanner.h
index d57fc292e..60983fe06 100644
--- a/src/lib/corelib/buildgraph/automoc.h
+++ b/src/lib/corelib/buildgraph/qtmocscanner.h
@@ -27,69 +27,50 @@
**
****************************************************************************/
-#ifndef QBS_AUTOMOC_H
-#define QBS_AUTOMOC_H
+#ifndef QBS_QTMOCSCANNER_H
+#define QBS_QTMOCSCANNER_H
-#include "forward_decls.h"
-
-#include <language/forward_decls.h>
+#include <language/language.h>
#include <logging/logger.h>
-#include <QObject>
+#include <QHash>
+#include <QScriptValue>
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+class QScriptContext;
+QT_END_NAMESPACE
-struct ScannerPlugin;
+class ScannerPlugin;
namespace qbs {
namespace Internal {
-class FileTag;
+
+class Artifact;
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
- * compile and link a moc_XXX.cpp file or not.
- *
- * This whole thing is an ugly hack, I know.
- */
-class AutoMoc : public QObject
+class QtMocScanner
{
- 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
- };
+ explicit QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue,
+ const Logger &logger);
+ ~QtMocScanner();
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);
- const QList<ScannerPlugin *> &cppScanners() const;
- const QList<ScannerPlugin *> &hppScanners() const;
+ void findIncludedMocCppFiles();
+ static QScriptValue js_apply(QScriptContext *ctx, QScriptEngine *engine, void *data);
+ QScriptValue apply(QScriptEngine *engine, const Artifact *artifact);
- mutable QList<ScannerPlugin *> m_cppScanners;
- mutable QList<ScannerPlugin *> m_hppScanners;
+ const ResolvedProductPtr &m_product;
+ QScriptValue m_targetScriptValue;
+ const Logger &m_logger;
ScanResultCache *m_scanResultCache;
- Logger m_logger;
+ QHash<QString, QString> m_includedMocCppFiles;
+ ScannerPlugin *m_cppScanner;
+ ScannerPlugin *m_hppScanner;
};
} // namespace Internal
} // namespace qbs
-#endif // QBS_AUTOMOC_H
+#endif // QBS_QTMOCSCANNER_H
diff --git a/src/lib/corelib/buildgraph/rescuableartifactdata.cpp b/src/lib/corelib/buildgraph/rescuableartifactdata.cpp
new file mode 100644
index 000000000..314b84008
--- /dev/null
+++ b/src/lib/corelib/buildgraph/rescuableartifactdata.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 "rescuableartifactdata.h"
+
+#include "command.h"
+
+#include <tools/persistence.h>
+
+namespace qbs {
+namespace Internal {
+
+RescuableArtifactData::~RescuableArtifactData()
+{
+}
+
+void RescuableArtifactData::load(PersistentPool &pool)
+{
+ pool.stream() >> timeStamp;
+
+ int c;
+ pool.stream() >> c;
+ for (int i = 0; i < c; ++i) {
+ ChildData cd;
+ pool.stream() >> cd.productName >> cd.childFilePath >> cd.addedByScanner;
+ children << cd;
+ }
+
+ commands = loadCommandList(pool.stream());
+}
+
+void RescuableArtifactData::store(PersistentPool &pool) const
+{
+ pool.stream() << timeStamp;
+
+ pool.stream() << children.count();
+ foreach (const ChildData &cd, children)
+ pool.stream() << cd.productName << cd.childFilePath << cd.addedByScanner;
+
+ storeCommandList(commands, pool.stream());
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/rescuableartifactdata.h b/src/lib/corelib/buildgraph/rescuableartifactdata.h
new file mode 100644
index 000000000..9c93b1cce
--- /dev/null
+++ b/src/lib/corelib/buildgraph/rescuableartifactdata.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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_RESCUABLEARTIFACTDATA_H
+#define QBS_RESCUABLEARTIFACTDATA_H
+
+#include "forward_decls.h"
+
+#include <tools/filetime.h>
+#include <tools/persistence.h>
+
+#include <QHash>
+#include <QList>
+
+namespace qbs {
+namespace Internal {
+
+class RescuableArtifactData : public PersistentObject
+{
+public:
+ ~RescuableArtifactData();
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+
+ struct ChildData
+ {
+ ChildData(const QString &p = QString(), const QString &c = QString(),
+ bool byScanner = false)
+ : productName(p), childFilePath(c), addedByScanner(byScanner)
+ {}
+ QString productName;
+ QString childFilePath;
+ bool addedByScanner;
+ };
+
+ FileTime timeStamp;
+ QList<ChildData> children;
+ QList<AbstractCommandPtr> commands;
+};
+typedef QHash<QString, RescuableArtifactData> AllRescuableArtifactData;
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard.
diff --git a/src/lib/corelib/buildgraph/rulegraph.cpp b/src/lib/corelib/buildgraph/rulegraph.cpp
index 7a2e114c4..7098004ef 100644
--- a/src/lib/corelib/buildgraph/rulegraph.cpp
+++ b/src/lib/corelib/buildgraph/rulegraph.cpp
@@ -44,7 +44,7 @@ void RuleGraph::build(const QSet<RulePtr> &rules, const FileTags &productFileTag
QMap<FileTag, QList<const Rule *> > inputFileTagToRule;
m_artifacts.reserve(rules.count());
foreach (const RulePtr &rule, rules) {
- foreach (const FileTag &fileTag, rule->staticOutputFileTags())
+ foreach (const FileTag &fileTag, rule->collectedOutputFileTags())
m_outputFileTagToRule[fileTag].append(rule.data());
insert(rule);
}
@@ -58,8 +58,11 @@ void RuleGraph::build(const QSet<RulePtr> &rules, const FileTags &productFileTag
inFileTags += rule->explicitlyDependsOn;
foreach (const FileTag &fileTag, inFileTags) {
inputFileTagToRule[fileTag].append(rule.data());
- foreach (const Rule * const consumingRule, m_outputFileTagToRule.value(fileTag)) {
- connect(rule.data(), consumingRule);
+ foreach (const Rule * const producingRule, m_outputFileTagToRule.value(fileTag)) {
+ if (!producingRule->collectedOutputFileTags().matches(
+ rule->excludedAuxiliaryInputs)) {
+ connect(rule.data(), producingRule);
+ }
}
}
}
@@ -74,30 +77,11 @@ void RuleGraph::build(const QSet<RulePtr> &rules, const FileTags &productFileTag
m_rootRules += r->ruleGraphId;
}
-QList<RuleConstPtr> RuleGraph::topSorted()
+void RuleGraph::accept(RuleGraphVisitor *visitor) const
{
- QSet<int> rootRules = m_rootRules;
- QList<RuleConstPtr> result;
- foreach (int rootIndex, rootRules) {
- RuleConstPtr rule = m_artifacts.at(rootIndex);
- QSet<const Rule *> seenRules;
- QList<const Rule *> rulePath;
- result.append(topSort(rule, &seenRules, &rulePath));
- }
-
- // remove duplicates from the result of our post-order traversal
- QSet<const Rule*> seenRules;
- seenRules.reserve(result.count());
- for (int i = 0; i < result.count();) {
- const Rule * const rule = result.at(i).data();
- if (seenRules.contains(rule))
- result.removeAt(i);
- else
- ++i;
- seenRules.insert(rule);
- }
-
- return result;
+ const RuleConstPtr nullParent;
+ foreach (int rootIndex, m_rootRules)
+ traverse(visitor, nullParent, m_artifacts.at(rootIndex));
}
void RuleGraph::dump() const
@@ -145,29 +129,13 @@ void RuleGraph::connect(const Rule *creatingRule, const Rule *consumingRule)
m_children[creatingRule->ruleGraphId].append(consumingRule->ruleGraphId);
}
-QList<RuleConstPtr> RuleGraph::topSort(const RuleConstPtr &rule, QSet<const Rule *> *seenRules,
- QList<const Rule *> *rulePath)
+void RuleGraph::traverse(RuleGraphVisitor *visitor, const RuleConstPtr &parentRule,
+ const RuleConstPtr &rule) const
{
- if (seenRules->contains(rule.data())) {
- QString pathstr;
- foreach (const Rule *r, *rulePath) {
- pathstr += QLatin1Char('\n') + r->toString() + QLatin1Char('\t')
- + r->prepareScript->location.toString();
- }
- throw ErrorInfo(Tr::tr("Cycle detected in rule dependencies: %1").arg(pathstr));
- }
-
- seenRules->insert(rule.data());
- rulePath->prepend(rule.data());
-
- QList<RuleConstPtr> result;
+ visitor->visit(parentRule, rule);
foreach (int childIndex, m_children.at(rule->ruleGraphId))
- result.append(topSort(m_artifacts.at(childIndex), seenRules, rulePath));
-
- result.append(rule);
- seenRules->remove(rule.data());
- rulePath->removeFirst();
- return result;
+ traverse(visitor, rule, m_artifacts.at(childIndex));
+ visitor->endVisit(rule);
}
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/rulegraph.h b/src/lib/corelib/buildgraph/rulegraph.h
index 2f9a42a33..375224449 100644
--- a/src/lib/corelib/buildgraph/rulegraph.h
+++ b/src/lib/corelib/buildgraph/rulegraph.h
@@ -42,13 +42,20 @@
namespace qbs {
namespace Internal {
+class RuleGraphVisitor
+{
+public:
+ virtual void visit(const RuleConstPtr &parentRule, const RuleConstPtr &rule) = 0;
+ virtual void endVisit(const RuleConstPtr &rule) { Q_UNUSED(rule); }
+};
+
class RuleGraph
{
public:
RuleGraph();
void build(const QSet<RulePtr> &rules, const FileTags &productFileTag);
- QList<RuleConstPtr> topSorted();
+ void accept(RuleGraphVisitor *visitor) const;
void dump() const;
@@ -56,8 +63,8 @@ private:
void dump_impl(QByteArray &indent, int rootIndex) const;
int insert(const RulePtr &rule);
void connect(const Rule *creatingRule, const Rule *consumingRule);
- 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/corelib/buildgraph/rulenode.cpp b/src/lib/corelib/buildgraph/rulenode.cpp
new file mode 100644
index 000000000..3d79cdea3
--- /dev/null
+++ b/src/lib/corelib/buildgraph/rulenode.cpp
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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();
+}
+
+void RuleNode::apply(const Logger &logger, const ArtifactSet &changedInputs,
+ ApplicationResult *result)
+{
+ bool hasAddedTags = false;
+ bool hasRemovedTags = false;
+ result->upToDate = changedInputs.isEmpty();
+
+ ProductBuildData::ArtifactSetByFileTag relevantArtifacts;
+ if (product->isMarkedForReapplication(m_rule)) {
+ QBS_CHECK(m_rule->multiplex);
+ result->upToDate = false;
+ product->unmarkForReapplication(m_rule);
+ if (logger.traceEnabled())
+ logger.qbsTrace() << "[BG] rule is marked for reapplication " << m_rule->toString();
+
+ foreach (Artifact *artifact, ArtifactSet::fromNodeSet(product->buildData->nodes)) {
+ if (m_rule->acceptsAsInput(artifact))
+ addArtifactToSet(artifact, relevantArtifacts);
+ }
+ } else {
+ foreach (const FileTag &tag, m_rule->inputs) {
+ if (product->addedArtifactsByFileTag(tag).count()) {
+ hasAddedTags = true;
+ result->upToDate = false;
+ }
+ if (product->removedArtifactsByFileTag(tag).count()) {
+ hasRemovedTags = true;
+ result->upToDate = false;
+ }
+ if (hasAddedTags && hasRemovedTags)
+ break;
+ }
+
+ relevantArtifacts = product->buildData->addedArtifactsByFileTag;
+ if (!changedInputs.isEmpty()) {
+ foreach (Artifact *artifact, changedInputs)
+ addArtifactToSet(artifact, relevantArtifacts);
+ }
+ }
+ if (result->upToDate)
+ return;
+ if (hasRemovedTags) {
+ ArtifactSet outputArtifactsToRemove;
+ foreach (const FileTag &tag, m_rule->inputs) {
+ foreach (Artifact *artifact, product->removedArtifactsByFileTag(tag)) {
+ foreach (Artifact *parent, ArtifactSet::fromNodeSet(artifact->parents)) {
+ if (!parent->transformer || parent->transformer->rule != m_rule
+ || !parent->transformer->inputs.contains(artifact)) {
+ // parent was not created by our rule.
+ continue;
+ }
+ outputArtifactsToRemove += parent;
+ }
+ }
+ }
+ RulesApplicator::handleRemovedRuleOutputs(outputArtifactsToRemove, logger);
+ }
+ if (!relevantArtifacts.isEmpty()) {
+ RulesApplicator applicator(product, relevantArtifacts, logger);
+ result->createdNodes = applicator.applyRuleInEvaluationContext(m_rule);
+ foreach (BuildGraphNode *node, result->createdNodes) {
+ if (Artifact *artifact = dynamic_cast<Artifact *>(node))
+ product->registerAddedArtifact(artifact);
+ }
+ }
+}
+
+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/corelib/buildgraph/rulenode.h b/src/lib/corelib/buildgraph/rulenode.h
new file mode 100644
index 000000000..b4f17a405
--- /dev/null
+++ b/src/lib/corelib/buildgraph/rulenode.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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; }
+
+ Type type() const { return RuleNodeType; }
+ void accept(BuildGraphVisitor *visitor);
+ QString toString() const;
+
+ struct ApplicationResult
+ {
+ bool upToDate;
+ QVector<BuildGraphNode *> createdNodes;
+ };
+
+ void apply(const Logger &logger, const ArtifactSet &changedInputs, ApplicationResult *result);
+
+protected:
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+
+private:
+ RuleConstPtr m_rule;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_RULENODE_H
diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp
index 70dce38c1..43c4efb45 100644
--- a/src/lib/corelib/buildgraph/rulesapplicator.cpp
+++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp
@@ -32,6 +32,7 @@
#include "buildgraph.h"
#include "productbuilddata.h"
#include "projectbuilddata.h"
+#include "qtmocscanner.h"
#include "rulesevaluationcontext.h"
#include "transformer.h"
#include <jsextensions/moduleproperties.h>
@@ -53,20 +54,31 @@ RulesApplicator::RulesApplicator(const ResolvedProductPtr &product,
ArtifactsPerFileTagMap &artifactsPerFileTag, const Logger &logger)
: m_product(product)
, m_artifactsPerFileTag(artifactsPerFileTag)
+ , m_mocScanner(0)
, m_logger(logger)
{
}
-void RulesApplicator::applyAllRules()
+RulesApplicator::~RulesApplicator()
{
+ delete m_mocScanner;
+}
+
+QVector<BuildGraphNode *> RulesApplicator::applyRuleInEvaluationContext(const RuleConstPtr &rule)
+{
+ m_createdArtifacts.clear();
RulesEvaluationContext::Scope s(m_product->topLevelProject()->buildData->evaluationContext.data());
- foreach (const RuleConstPtr &rule, m_product->topSortedRules())
- applyRule(rule);
+ applyRule(rule);
+ return m_createdArtifacts;
}
void RulesApplicator::applyRule(const RuleConstPtr &rule)
{
m_rule = rule;
+ if (rule->name == QLatin1String("QtCoreMocRule")) {
+ delete m_mocScanner;
+ m_mocScanner = new QtMocScanner(m_product, scope(), m_logger);
+ }
QScriptValue prepareScriptContext = engine()->newObject();
PrepareScriptObserver observer(engine());
setupScriptEngineForFile(engine(), m_rule->prepareScript->fileContext, scope());
@@ -89,13 +101,44 @@ void RulesApplicator::applyRule(const RuleConstPtr &rule)
}
}
+void RulesApplicator::handleRemovedRuleOutputs(ArtifactSet outputArtifactsToRemove,
+ const Logger &logger)
+{
+ ArtifactSet artifactsToRemove;
+ foreach (Artifact *removedArtifact, outputArtifactsToRemove) {
+ if (logger.traceEnabled()) {
+ logger.qbsTrace() << "[BG] dynamic rule removed output artifact "
+ << removedArtifact->toString();
+ }
+ removedArtifact->product->topLevelProject()
+ ->buildData->removeArtifactAndExclusiveDependents(removedArtifact, logger, true,
+ &artifactsToRemove);
+ }
+ // parents of removed artifacts must update their transformers
+ foreach (Artifact *removedArtifact, artifactsToRemove) {
+ foreach (Artifact *parent, removedArtifact->parentArtifacts())
+ parent->product->registerArtifactWithChangedInputs(parent);
+ }
+ qDeleteAll(artifactsToRemove);
+}
+
static void copyProperty(const QString &name, const QScriptValue &src, QScriptValue dst)
{
dst.setProperty(name, src.property(name));
}
-void RulesApplicator::doApply(const ArtifactSet &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(ArtifactSet inputArtifacts, QScriptValue &prepareScriptContext)
{
evalContext()->checkForCancelation();
@@ -108,31 +151,49 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts,
QList<QPair<const RuleArtifact *, Artifact *> > ruleArtifactArtifactMap;
QList<Artifact *> outputArtifacts;
- ArtifactSet usingArtifacts;
if (!m_rule->usings.isEmpty()) {
const FileTags usingsFileTags = m_rule->usings;
foreach (const ResolvedProductPtr &dep, m_product->dependencies) {
QBS_CHECK(dep->buildData);
ArtifactSet artifactsToCheck;
- foreach (Artifact *targetArtifact, dep->buildData->targetArtifacts)
+ foreach (Artifact *targetArtifact, dep->buildData->targetArtifacts())
artifactsToCheck.unite(targetArtifact->transformer->outputs);
foreach (Artifact *artifact, artifactsToCheck) {
if (artifact->fileTags.matches(usingsFileTags))
- usingArtifacts.insert(artifact);
+ inputArtifacts.insert(artifact);
}
}
}
m_transformer.clear();
// create the output artifacts from the set of input artifacts
+ Transformer::setupInputs(prepareScriptContext, inputArtifacts, m_rule->module->name);
+ copyProperty(QLatin1String("inputs"), prepareScriptContext, scope());
+ if (m_rule->multiplex) {
+ // ### awful! Revisit how the "input" property is set up!
+ copyProperty(QLatin1String("input"), prepareScriptContext, scope());
+ }
copyProperty(QLatin1String("product"), prepareScriptContext, scope());
copyProperty(QLatin1String("project"), prepareScriptContext, scope());
- foreach (const RuleArtifactConstPtr &ruleArtifact, m_rule->artifacts) {
- Artifact * const outputArtifact = createOutputArtifact(ruleArtifact, inputArtifacts);
- outputArtifacts << outputArtifact;
- ruleArtifactArtifactMap << qMakePair(ruleArtifact.data(), outputArtifact);
+ if (m_rule->isDynamic()) {
+ outputArtifacts = runOutputArtifactsScript(inputArtifacts,
+ ScriptEngine::argumentList(m_rule->outputArtifactsScript->argumentNames,
+ scope()));
+ ArtifactSet newOutputs = ArtifactSet::fromNodeList(outputArtifacts);
+ const ArtifactSet oldOutputs = collectOldOutputArtifacts(inputArtifacts);
+ handleRemovedRuleOutputs(oldOutputs - newOutputs, m_logger);
+ } else {
+ foreach (const RuleArtifactConstPtr &ruleArtifact, m_rule->artifacts) {
+ Artifact * const outputArtifact
+ = createOutputArtifactFromRuleArtifact(ruleArtifact, inputArtifacts);
+ outputArtifacts << outputArtifact;
+ ruleArtifactArtifactMap << qMakePair(ruleArtifact.data(), outputArtifact);
+ }
}
+ if (outputArtifacts.isEmpty())
+ return;
+
foreach (Artifact *outputArtifact, outputArtifacts) {
// insert the output artifacts into the pool of artifacts
foreach (const FileTag &fileTag, outputArtifact->fileTags)
@@ -143,21 +204,12 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts,
foreach (Artifact *dependency, m_artifactsPerFileTag.value(fileTag))
loggedConnect(outputArtifact, dependency, m_logger);
- // Transformer setup
- for (ArtifactSet::const_iterator it = usingArtifacts.constBegin();
- it != usingArtifacts.constEnd(); ++it)
- {
- Artifact *dep = *it;
- loggedConnect(outputArtifact, dep, m_logger);
- m_transformer->inputs.insert(dep);
- }
m_transformer->outputs.insert(outputArtifact);
-
- m_product->topLevelProject()->buildData->artifactsThatMustGetNewTransformers
- -= outputArtifact;
+ outputArtifact->product->unregisterArtifactWithChangedInputs(outputArtifact);
}
- m_transformer->setupInputs(engine(), prepareScriptContext);
+ if (inputArtifacts != m_transformer->inputs)
+ m_transformer->setupInputs(prepareScriptContext);
// change the transformer outputs according to the bindings in Artifact
QScriptValue scriptValue;
@@ -227,21 +279,44 @@ void RulesApplicator::setupScriptEngineForArtifact(Artifact *artifact)
scriptValue.setProperty(QLatin1String("baseName"), inBaseName);
scriptValue.setProperty(QLatin1String("completeBaseName"), inCompleteBaseName);
scriptValue.setProperty(QLatin1String("baseDir"), basedir);
+ scriptValue.setProperty(QLatin1String("fileTags"),
+ engine()->toScriptValue(artifact->fileTags.toStringList()));
+ attachPointerTo(scriptValue, artifact);
scope().setProperty(QLatin1String("input"), scriptValue);
Q_ASSERT_X(scriptValue.strictlyEquals(engine()->evaluate(QLatin1String("input"))),
"BG", "The input object is not in current scope.");
}
-Artifact *RulesApplicator::createOutputArtifact(const RuleArtifactConstPtr &ruleArtifact,
- const ArtifactSet &inputArtifacts)
+ArtifactSet RulesApplicator::collectOldOutputArtifacts(const ArtifactSet &inputArtifacts) const
+{
+ ArtifactSet result;
+ foreach (Artifact *a, inputArtifacts) {
+ foreach (Artifact *p, a->parentArtifacts()) {
+ QBS_CHECK(p->transformer);
+ if (p->transformer->rule == m_rule && p->transformer->inputs.contains(a))
+ result += p;
+ }
+ }
+ return result;
+}
+
+Artifact *RulesApplicator::createOutputArtifactFromRuleArtifact(
+ const RuleArtifactConstPtr &ruleArtifact, const ArtifactSet &inputArtifacts)
{
QScriptValue scriptValue = engine()->evaluate(ruleArtifact->fileName);
if (Q_UNLIKELY(engine()->hasErrorOrException(scriptValue)))
throw ErrorInfo(Tr::tr("Error in Rule.Artifact fileName: ") + scriptValue.toString());
QString outputPath = scriptValue.toString();
+ return createOutputArtifact(outputPath, ruleArtifact->fileTags, ruleArtifact->alwaysUpdated,
+ inputArtifacts);
+}
- // Don't let the output artifact "escape" its build dir
+Artifact *RulesApplicator::createOutputArtifact(const QString &filePath, const FileTags &fileTags,
+ bool alwaysUpdated, const ArtifactSet &inputArtifacts)
+{
+ QString outputPath = filePath;
+ // don't let the output artifact "escape" its build dir
outputPath.replace(QLatin1String(".."), QLatin1String("dotdot"));
outputPath = resolveOutPath(outputPath);
@@ -288,15 +363,17 @@ Artifact *RulesApplicator::createOutputArtifact(const RuleArtifactConstPtr &rule
throw ErrorInfo(e);
}
}
- outputArtifact->fileTags += ruleArtifact->fileTags;
+ outputArtifact->fileTags += fileTags;
+ outputArtifact->clearTimestamp();
} else {
outputArtifact = new Artifact;
outputArtifact->artifactType = Artifact::Generated;
outputArtifact->setFilePath(outputPath);
- outputArtifact->fileTags = ruleArtifact->fileTags;
- outputArtifact->alwaysUpdated = ruleArtifact->alwaysUpdated;
+ outputArtifact->fileTags = fileTags;
+ outputArtifact->alwaysUpdated = alwaysUpdated;
outputArtifact->properties = m_product->properties;
insertArtifact(m_product, outputArtifact, m_logger);
+ m_createdArtifacts += outputArtifact;
}
if (outputArtifact->fileTags.isEmpty())
@@ -326,6 +403,47 @@ Artifact *RulesApplicator::createOutputArtifact(const RuleArtifactConstPtr &rule
return outputArtifact;
}
+QList<Artifact *> RulesApplicator::runOutputArtifactsScript(const ArtifactSet &inputArtifacts,
+ const QScriptValueList &args)
+{
+ QList<Artifact *> lst;
+ QScriptValue fun = engine()->evaluate(m_rule->outputArtifactsScript->sourceCode);
+ if (!fun.isFunction())
+ throw ErrorInfo(QLatin1String("Function expected."),
+ m_rule->outputArtifactsScript->location);
+ QScriptValue res = fun.call(QScriptValue(), args);
+ if (res.isError() || engine()->hasUncaughtException())
+ throw ErrorInfo(Tr::tr("Error while calling Rule.outputArtifacts: %1").arg(res.toString()),
+ m_rule->outputArtifactsScript->location);
+ if (!res.isArray())
+ throw ErrorInfo(Tr::tr("Rule.outputArtifacts must return an array of objects."),
+ m_rule->outputArtifactsScript->location);
+ const quint32 c = res.property(QLatin1String("length")).toUInt32();
+ for (quint32 i = 0; i < c; ++i)
+ lst += createOutputArtifactFromScriptValue(res.property(i), inputArtifacts);
+ return lst;
+}
+
+Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const QScriptValue &obj,
+ const ArtifactSet &inputArtifacts)
+{
+ QBS_CHECK(obj.isObject());
+ const QString filePath = obj.property(QLatin1String("filePath")).toVariant().toString();
+ const FileTags fileTags = FileTags::fromStringList(
+ obj.property(QLatin1String("fileTags")).toVariant().toStringList());
+ const QVariant alwaysUpdatedVar = obj.property(QLatin1String("alwaysUpdated")).toVariant();
+ const bool alwaysUpdated = alwaysUpdatedVar.isValid() ? alwaysUpdatedVar.toBool() : true;
+ Artifact *output = createOutputArtifact(filePath, fileTags, alwaysUpdated, inputArtifacts);
+ const FileTags explicitlyDependsOn = FileTags::fromStringList(
+ obj.property(QLatin1String("explicitlyDependsOn")).toVariant().toStringList());
+ foreach (const FileTag &tag, explicitlyDependsOn) {
+ foreach (Artifact *dependency, m_product->lookupArtifactsByFileTag(tag)) {
+ loggedConnect(output, dependency, m_logger);
+ }
+ }
+ return output;
+}
+
QString RulesApplicator::resolveOutPath(const QString &path) const
{
QString buildDir = m_product->topLevelProject()->buildDirectory;
diff --git a/src/lib/corelib/buildgraph/rulesapplicator.h b/src/lib/corelib/buildgraph/rulesapplicator.h
index 9ea54dfe7..e5813834a 100644
--- a/src/lib/corelib/buildgraph/rulesapplicator.h
+++ b/src/lib/corelib/buildgraph/rulesapplicator.h
@@ -35,29 +35,41 @@
#include <language/forward_decls.h>
#include <logging/logger.h>
-#include <QMap>
+#include <QHash>
#include <QScriptValue>
#include <QString>
+#include <QVector>
namespace qbs {
namespace Internal {
+class BuildGraphNode;
+class QtMocScanner;
class ScriptEngine;
-typedef QMap<FileTag, ArtifactSet> ArtifactsPerFileTagMap;
+typedef QHash<FileTag, ArtifactSet> ArtifactsPerFileTagMap;
class RulesApplicator
{
public:
RulesApplicator(const ResolvedProductPtr &product, ArtifactsPerFileTagMap &artifactsPerFileTag,
const Logger &logger);
- void applyAllRules();
+ ~RulesApplicator();
+ QVector<BuildGraphNode *> applyRuleInEvaluationContext(const RuleConstPtr &rule);
void applyRule(const RuleConstPtr &rule);
+ static void handleRemovedRuleOutputs(ArtifactSet artifactsToRemove, const Logger &logger);
private:
- void doApply(const ArtifactSet &inputArtifacts, QScriptValue &prepareScriptContext);
+ void doApply(ArtifactSet inputArtifacts, QScriptValue &prepareScriptContext);
void setupScriptEngineForArtifact(Artifact *artifact);
- Artifact *createOutputArtifact(const RuleArtifactConstPtr &ruleArtifact,
- const ArtifactSet &inputArtifacts);
+ ArtifactSet collectOldOutputArtifacts(const ArtifactSet &inputArtifacts) const;
+ Artifact *createOutputArtifactFromRuleArtifact(const RuleArtifactConstPtr &ruleArtifact,
+ const ArtifactSet &inputArtifacts);
+ Artifact *createOutputArtifact(const QString &filePath, const FileTags &fileTags,
+ bool alwaysUpdated, const ArtifactSet &inputArtifacts);
+ QList<Artifact *> runOutputArtifactsScript(const ArtifactSet &inputArtifacts,
+ const QScriptValueList &args);
+ Artifact *createOutputArtifactFromScriptValue(const QScriptValue &obj,
+ const ArtifactSet &inputArtifacts);
QString resolveOutPath(const QString &path) const;
RulesEvaluationContextPtr evalContext() const;
ScriptEngine *engine() const;
@@ -65,9 +77,11 @@ private:
const ResolvedProductPtr m_product;
ArtifactsPerFileTagMap &m_artifactsPerFileTag;
+ QVector<BuildGraphNode *> m_createdArtifacts;
RuleConstPtr m_rule;
TransformerPtr m_transformer;
+ QtMocScanner *m_mocScanner;
Logger m_logger;
};
diff --git a/src/lib/corelib/buildgraph/timestampsupdater.cpp b/src/lib/corelib/buildgraph/timestampsupdater.cpp
index c22e9f96c..2ba6bc8ee 100644
--- a/src/lib/corelib/buildgraph/timestampsupdater.cpp
+++ b/src/lib/corelib/buildgraph/timestampsupdater.cpp
@@ -54,7 +54,7 @@ public:
// For target artifacts, we have to update the on-disk timestamp, because
// the executor will look at it.
- foreach (Artifact * const targetArtifact, product->buildData->targetArtifacts) {
+ foreach (Artifact * const targetArtifact, product->buildData->targetArtifacts()) {
if (FileInfo(targetArtifact->filePath()).exists())
QFile(targetArtifact->filePath()).open(QIODevice::WriteOnly | QIODevice::Append);
}
diff --git a/src/lib/corelib/buildgraph/transformer.cpp b/src/lib/corelib/buildgraph/transformer.cpp
index becb110ba..011ff0fb0 100644
--- a/src/lib/corelib/buildgraph/transformer.cpp
+++ b/src/lib/corelib/buildgraph/transformer.cpp
@@ -48,7 +48,6 @@ Transformer::Transformer()
Transformer::~Transformer()
{
- qDeleteAll(commands);
}
QScriptValue Transformer::translateFileConfig(QScriptEngine *scriptEngine, Artifact *artifact, const QString &defaultModuleName)
@@ -92,9 +91,10 @@ ResolvedProductPtr Transformer::product() const
return (*outputs.begin())->product;
}
-void Transformer::setupInputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue)
+void Transformer::setupInputs(QScriptValue targetScriptValue, const ArtifactSet &inputs,
+ const QString &defaultModuleName)
{
- const QString &defaultModuleName = rule->module->name;
+ QScriptEngine *const scriptEngine = targetScriptValue.engine();
QScriptValue scriptValue = translateInOutputs(scriptEngine, inputs, defaultModuleName);
targetScriptValue.setProperty(QLatin1String("inputs"), scriptValue);
if (inputs.count() == 1) {
@@ -109,6 +109,11 @@ void Transformer::setupInputs(QScriptEngine *scriptEngine, QScriptValue targetSc
}
}
+void Transformer::setupInputs(QScriptValue targetScriptValue)
+{
+ setupInputs(targetScriptValue, inputs, rule->module->name);
+}
+
void Transformer::setupOutputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue)
{
const QString &defaultModuleName = rule->module->name;
@@ -126,17 +131,17 @@ void Transformer::setupOutputs(QScriptEngine *scriptEngine, QScriptValue targetS
}
}
-static AbstractCommand *createCommandFromScriptValue(const QScriptValue &scriptValue,
- const CodeLocation &codeLocation)
+static AbstractCommandPtr createCommandFromScriptValue(const QScriptValue &scriptValue,
+ const CodeLocation &codeLocation)
{
+ AbstractCommandPtr cmdBase;
if (scriptValue.isUndefined() || !scriptValue.isValid())
- return 0;
- AbstractCommand *cmdBase = 0;
+ return cmdBase;
QString className = scriptValue.property(QLatin1String("className")).toString();
if (className == QLatin1String("Command"))
- cmdBase = new ProcessCommand;
+ cmdBase = ProcessCommand::create();
else if (className == QLatin1String("JavaScriptCommand"))
- cmdBase = new JavaScriptCommand;
+ cmdBase = JavaScriptCommand::create();
if (cmdBase)
cmdBase->fillFromScriptValue(&scriptValue, codeLocation);
return cmdBase;
@@ -162,20 +167,19 @@ void Transformer::createCommands(const ScriptFunctionConstPtr &script,
CodeLocation(script->location.fileName(),
script->location.line() + engine->uncaughtExceptionLineNumber() - 1));
- qDeleteAll(commands);
commands.clear();
if (scriptValue.isArray()) {
const int count = scriptValue.property(QLatin1String("length")).toInt32();
for (qint32 i = 0; i < count; ++i) {
QScriptValue item = scriptValue.property(i);
if (item.isValid() && !item.isUndefined()) {
- AbstractCommand *cmd = createCommandFromScriptValue(item, script->location);
+ const AbstractCommandPtr cmd = createCommandFromScriptValue(item, script->location);
if (cmd)
commands += cmd;
}
}
} else {
- AbstractCommand *cmd = createCommandFromScriptValue(scriptValue, script->location);
+ const AbstractCommandPtr cmd = createCommandFromScriptValue(scriptValue, script->location);
if (cmd)
commands += cmd;
}
@@ -223,15 +227,7 @@ void Transformer::load(PersistentPool &pool)
}
propertiesRequestedFromArtifactInPrepareScript.insert(artifactName, list);
}
- int cmdType;
- pool.stream() >> count;
- commands.reserve(count);
- while (--count >= 0) {
- pool.stream() >> cmdType;
- AbstractCommand *cmd = AbstractCommand::createByType(static_cast<AbstractCommand::CommandType>(cmdType));
- cmd->load(pool.stream());
- commands += cmd;
- }
+ commands = loadCommandList(pool.stream());
}
static void storePropertyList(PersistentPool &pool, const PropertyList &list)
@@ -263,11 +259,7 @@ void Transformer::store(PersistentPool &pool) const
pool.stream() << p.value; // kind is always PropertyInModule
}
}
- pool.stream() << commands.count();
- foreach (AbstractCommand *cmd, commands) {
- pool.stream() << int(cmd->type());
- cmd->store(pool.stream());
- }
+ storeCommandList(commands, pool.stream());
}
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/transformer.h b/src/lib/corelib/buildgraph/transformer.h
index 3ef30cc29..ee9ee4002 100644
--- a/src/lib/corelib/buildgraph/transformer.h
+++ b/src/lib/corelib/buildgraph/transformer.h
@@ -56,7 +56,7 @@ public:
ArtifactSet inputs; // Subset of "children of all outputs".
ArtifactSet outputs;
RuleConstPtr rule;
- QList<AbstractCommand *> commands;
+ QList<AbstractCommandPtr> commands;
PropertyList propertiesRequestedInPrepareScript;
PropertyList propertiesRequestedInCommands;
QHash<QString, PropertyList> propertiesRequestedFromArtifactInPrepareScript;
@@ -69,7 +69,9 @@ public:
const QString &defaultModuleName);
ResolvedProductPtr product() const;
- void setupInputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue);
+ static void setupInputs(QScriptValue targetScriptValue, const ArtifactSet &inputs,
+ const QString &defaultModuleName);
+ void setupInputs(QScriptValue targetScriptValue);
void setupOutputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue);
void createCommands(const ScriptFunctionConstPtr &script,
const RulesEvaluationContextPtr &evalContext, const QScriptValueList &args);
diff --git a/src/lib/corelib/buildgraph/tst_buildgraph.cpp b/src/lib/corelib/buildgraph/tst_buildgraph.cpp
index a159c2938..4f2aa3ebf 100644
--- a/src/lib/corelib/buildgraph/tst_buildgraph.cpp
+++ b/src/lib/corelib/buildgraph/tst_buildgraph.cpp
@@ -73,7 +73,7 @@ ResolvedProductConstPtr TestBuildGraph::productWithDirectCycle()
child->children.insert(root);
const ResolvedProductPtr product = ResolvedProduct::create();
product->buildData.reset(new ProductBuildData);
- product->buildData->targetArtifacts.insert(root);
+ product->buildData->roots.insert(root);
return product;
}
@@ -88,7 +88,7 @@ ResolvedProductConstPtr TestBuildGraph::productWithLessDirectCycle()
grandchild->children.insert(root);
const ResolvedProductPtr product = ResolvedProduct::create();
product->buildData.reset(new ProductBuildData);
- product->buildData->targetArtifacts << root;
+ product->buildData->roots << root;
return product;
}
@@ -101,7 +101,7 @@ ResolvedProductConstPtr TestBuildGraph::productWithNoCycle()
root2->children.insert(root);
const ResolvedProductPtr product = ResolvedProduct::create();
product->buildData.reset(new ProductBuildData);
- product->buildData->targetArtifacts << root << root2;
+ product->buildData->roots << root << root2;
return product;
}
diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs
index 6ebda9b2c..c2e84ca81 100644
--- a/src/lib/corelib/corelib.qbs
+++ b/src/lib/corelib/corelib.qbs
@@ -73,12 +73,13 @@ QbsLibrary {
"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",
@@ -93,6 +94,8 @@ QbsLibrary {
"inputartifactscanner.h",
"jscommandexecutor.cpp",
"jscommandexecutor.h",
+ "nodeset.cpp",
+ "nodeset.h",
"processcommandexecutor.cpp",
"processcommandexecutor.h",
"productbuilddata.cpp",
@@ -101,8 +104,14 @@ QbsLibrary {
"productinstaller.h",
"projectbuilddata.cpp",
"projectbuilddata.h",
+ "qtmocscanner.cpp",
+ "qtmocscanner.h",
+ "rescuableartifactdata.cpp",
+ "rescuableartifactdata.h",
"rulegraph.cpp",
"rulegraph.h",
+ "rulenode.cpp",
+ "rulenode.h",
"rulesapplicator.cpp",
"rulesapplicator.h",
"rulesevaluationcontext.cpp",
diff --git a/src/lib/corelib/language/builtindeclarations.cpp b/src/lib/corelib/language/builtindeclarations.cpp
index 54d221d96..5b2b09bf8 100644
--- a/src/lib/corelib/language/builtindeclarations.cpp
+++ b/src/lib/corelib/language/builtindeclarations.cpp
@@ -364,10 +364,19 @@ void BuiltinDeclarations::addRuleItem()
PropertyDeclaration decl(QLatin1String("multiplex"), PropertyDeclaration::Boolean);
decl.initialValueSource = QLatin1String("false");
item << decl;
+ item << PropertyDeclaration(QLatin1String("name"), PropertyDeclaration::String);
item << PropertyDeclaration(QLatin1String("inputs"), PropertyDeclaration::StringList);
+ item << PropertyDeclaration(QLatin1String("outputFileTags"), PropertyDeclaration::StringList);
+ decl = PropertyDeclaration(QLatin1String("outputArtifacts"), PropertyDeclaration::Verbatim);
+ decl.functionArgumentNames
+ << QLatin1String("project") << QLatin1String("product")
+ << QLatin1String("inputs") << QLatin1String("input");
+ item << decl;
item << PropertyDeclaration(QLatin1String("usings"), PropertyDeclaration::StringList);
item << PropertyDeclaration(QLatin1String("auxiliaryInputs"),
PropertyDeclaration::StringList);
+ item << PropertyDeclaration(QLatin1String("excludedAuxiliaryInputs"),
+ PropertyDeclaration::StringList);
item << PropertyDeclaration(QLatin1String("explicitlyDependsOn"),
PropertyDeclaration::StringList);
item << prepareScriptProperty();
diff --git a/src/lib/corelib/language/language.cpp b/src/lib/corelib/language/language.cpp
index 676551129..170d04863 100644
--- a/src/lib/corelib/language/language.cpp
+++ b/src/lib/corelib/language/language.cpp
@@ -35,6 +35,7 @@
#include <buildgraph/productbuilddata.h>
#include <buildgraph/projectbuilddata.h>
#include <buildgraph/rulegraph.h> // TODO: Move to language?
+#include <buildgraph/transformer.h>
#include <jsextensions/jsextensions.h>
#include <logging/translator.h>
#include <tools/hostosinfo.h>
@@ -290,6 +291,11 @@ bool operator==(const ResolvedFileContext &a, const ResolvedFileContext &b)
* This is mostly needed for diagnostics.
*/
+bool ScriptFunction::isValid() const
+{
+ return location.isValid();
+}
+
void ScriptFunction::load(PersistentPool &pool)
{
pool.stream()
@@ -354,12 +360,18 @@ static bool modulesAreEqual(const ResolvedModuleConstPtr &m1, const ResolvedModu
QString Rule::toString() const
{
- QStringList outputTagsSorted = staticOutputFileTags().toStringList();
+ QStringList outputTagsSorted = collectedOutputFileTags().toStringList();
outputTagsSorted.sort();
QStringList inputTagsSorted = inputs.toStringList();
inputTagsSorted.sort();
- return QLatin1Char('[') + inputTagsSorted.join(QLatin1String(",")) + QLatin1String(" -> ")
- + outputTagsSorted.join(QLatin1String(",")) + QLatin1Char(']');
+ return QLatin1Char('[') + outputTagsSorted.join(QLatin1String(","))
+ + QLatin1String("][")
+ + inputTagsSorted.join(QLatin1String(",")) + QLatin1Char(']');
+}
+
+bool Rule::acceptsAsInput(Artifact *artifact) const
+{
+ return artifact->fileTags.matches(inputs);
}
FileTags Rule::staticOutputFileTags() const
@@ -370,13 +382,27 @@ FileTags Rule::staticOutputFileTags() const
return result;
}
+FileTags Rule::collectedOutputFileTags() const
+{
+ return outputFileTags.isEmpty() ? staticOutputFileTags() : outputFileTags;
+}
+
+bool Rule::isDynamic() const
+{
+ return outputArtifactsScript->isValid();
+}
+
void Rule::load(PersistentPool &pool)
{
+ name = pool.idLoadString();
prepareScript = pool.idLoadS<ScriptFunction>();
+ outputArtifactsScript = pool.idLoadS<ScriptFunction>();
module = pool.idLoadS<ResolvedModule>();
pool.stream()
>> inputs
+ >> outputFileTags
>> auxiliaryInputs
+ >> excludedAuxiliaryInputs
>> usings
>> explicitlyDependsOn
>> multiplex;
@@ -386,11 +412,15 @@ void Rule::load(PersistentPool &pool)
void Rule::store(PersistentPool &pool) const
{
+ pool.storeString(name);
pool.store(prepareScript);
+ pool.store(outputArtifactsScript);
pool.store(module);
pool.stream()
<< inputs
+ << outputFileTags
<< auxiliaryInputs
+ << excludedAuxiliaryInputs
<< usings
<< explicitlyDependsOn
<< multiplex;
@@ -407,6 +437,14 @@ ResolvedProduct::~ResolvedProduct()
{
}
+void ResolvedProduct::accept(BuildGraphVisitor *visitor) const
+{
+ if (!buildData)
+ return;
+ foreach (BuildGraphNode * const node, buildData->roots)
+ node->accept(visitor);
+}
+
/*!
* \brief Returns all files of all groups as source artifacts.
* This includes the expanded list of wildcards.
@@ -661,19 +699,100 @@ void ResolvedProduct::setupRunEnvironment(ScriptEngine *engine, const QProcessEn
topLevelProject(), env);
}
-const QList<RuleConstPtr> &ResolvedProduct::topSortedRules() const
+void ResolvedProduct::registerAddedFileTag(const FileTag &fileTag, Artifact *artifact)
+{
+ QBS_CHECK(buildData);
+ QBS_CHECK(artifact->product == this);
+ if (buildData->removedArtifactsByFileTag.value(fileTag).contains(artifact)) {
+ buildData->removedArtifactsByFileTag[fileTag].remove(artifact);
+ return;
+ }
+ buildData->addedArtifactsByFileTag[fileTag].insert(artifact);
+}
+
+void ResolvedProduct::registerAddedArtifact(Artifact *artifact)
+{
+ QBS_CHECK(buildData);
+ QBS_CHECK(artifact->product == this);
+ foreach (const FileTag &tag, artifact->fileTags)
+ registerAddedFileTag(tag, artifact);
+}
+
+void ResolvedProduct::unregisterAddedArtifact(Artifact *artifact)
+{
+ ProductBuildData::ArtifactSetByFileTag::Iterator it
+ = buildData->addedArtifactsByFileTag.begin();
+ while (it != buildData->addedArtifactsByFileTag.end()) {
+ ArtifactSet &artifacts = it.value();
+ artifacts.remove(artifact);
+ if (artifacts.isEmpty())
+ it = buildData->addedArtifactsByFileTag.erase(it);
+ else
+ ++it;
+ }
+}
+
+void ResolvedProduct::registerRemovedFileTag(const FileTag &fileTag, Artifact *artifact)
{
QBS_CHECK(buildData);
- if (buildData->topSortedRules.isEmpty()) {
- RuleGraph ruleGraph;
- ruleGraph.build(rules, fileTags);
-// ruleGraph.dump();
- buildData->topSortedRules = ruleGraph.topSorted();
-// int i=0;
-// foreach (RulePtr r, m_topSortedRules)
-// qDebug() << ++i << r->toString() << (void*)r.data();
+ QBS_CHECK(artifact->product == this);
+ if (buildData->addedArtifactsByFileTag.value(fileTag).contains(artifact)) {
+ buildData->addedArtifactsByFileTag[fileTag].remove(artifact);
+ return;
+ }
+ buildData->removedArtifactsByFileTag[fileTag].insert(artifact);
+}
+
+void ResolvedProduct::registerArtifactWithChangedInputs(Artifact *artifact)
+{
+ QBS_CHECK(buildData);
+ QBS_CHECK(artifact->product == this);
+ QBS_CHECK(artifact->transformer);
+ if (artifact->transformer->rule->multiplex) {
+ // Reapplication of rules only makes sense for multiplex rules (e.g. linker).
+ buildData->artifactsWithChangedInputsPerRule[artifact->transformer->rule] += artifact;
+ }
+}
+
+void ResolvedProduct::unregisterArtifactWithChangedInputs(Artifact *artifact)
+{
+ QBS_CHECK(buildData);
+ QBS_CHECK(artifact->product == this);
+ QBS_CHECK(artifact->transformer);
+ buildData->artifactsWithChangedInputsPerRule[artifact->transformer->rule] -= artifact;
+}
+
+void ResolvedProduct::unmarkForReapplication(const RuleConstPtr &rule)
+{
+ QBS_CHECK(buildData);
+ buildData->artifactsWithChangedInputsPerRule.remove(rule);
+}
+
+const ArtifactSet ResolvedProduct::addedArtifactsByFileTag(const FileTag &tag) const
+{
+ return buildData->addedArtifactsByFileTag.value(tag);
+}
+
+const ArtifactSet ResolvedProduct::removedArtifactsByFileTag(const FileTag &tag) const
+{
+ return buildData->removedArtifactsByFileTag.value(tag);
+}
+
+bool ResolvedProduct::isMarkedForReapplication(const RuleConstPtr &rule) const
+{
+ return !buildData->artifactsWithChangedInputsPerRule.value(rule).isEmpty();
+}
+
+ArtifactSet ResolvedProduct::lookupArtifactsByFileTag(const FileTag &tag) const
+{
+ QBS_CHECK(buildData);
+ // ### slow. improve.
+ ArtifactSet result;
+ foreach (Artifact * const a, ArtifactSet::fromNodeSet(buildData->nodes)) {
+ if (a->fileTags.contains(tag))
+ result += a;
}
- return buildData->topSortedRules;
+ return result;
}
TopLevelProject *ResolvedProduct::topLevelProject() const
@@ -684,13 +803,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;
@@ -702,7 +821,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);
}
@@ -713,6 +832,14 @@ ResolvedProject::ResolvedProject() : enabled(true), m_topLevelProject(0)
{
}
+void ResolvedProject::accept(BuildGraphVisitor *visitor) const
+{
+ foreach (const ResolvedProductPtr &product, products)
+ product->accept(visitor);
+ foreach (const ResolvedProjectPtr &subProject, subProjects)
+ subProject->accept(visitor);
+}
+
TopLevelProject *ResolvedProject::topLevelProject()
{
if (m_topLevelProject)
@@ -756,8 +883,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);
}
@@ -1097,8 +1229,11 @@ bool operator==(const Rule &r1, const Rule &r2)
return r1.module->name == r2.module->name
&& r1.prepareScript->sourceCode == r2.prepareScript->sourceCode
+ && r1.outputArtifactsScript->sourceCode == r2.outputArtifactsScript->sourceCode
&& r1.inputs == r2.inputs
+ && r1.outputFileTags == r2.outputFileTags
&& r1.auxiliaryInputs == r2.auxiliaryInputs
+ && r1.excludedAuxiliaryInputs == r2.excludedAuxiliaryInputs
&& r1.usings == r2.usings
&& r1.explicitlyDependsOn == r2.explicitlyDependsOn
&& r1.multiplex == r2.multiplex;
diff --git a/src/lib/corelib/language/language.h b/src/lib/corelib/language/language.h
index b62c0a1ab..c1e4f63fb 100644
--- a/src/lib/corelib/language/language.h
+++ b/src/lib/corelib/language/language.h
@@ -34,6 +34,7 @@
#include "forward_decls.h"
#include "jsimports.h"
#include "propertymapinternal.h"
+#include <buildgraph/artifactset.h>
#include <buildgraph/forward_decls.h>
#include <tools/codelocation.h>
#include <tools/fileinfo.h>
@@ -61,6 +62,7 @@ QT_END_NAMESPACE
namespace qbs {
namespace Internal {
class BuildGraphLoader;
+class BuildGraphVisitor;
class FileTagger : public PersistentObject
{
@@ -235,6 +237,8 @@ public:
ResolvedFileContextConstPtr fileContext;
mutable QScriptValue scriptFunction; // cache
+ bool isValid() const;
+
private:
ScriptFunction() {}
@@ -279,19 +283,26 @@ public:
static RulePtr create() { return RulePtr(new Rule); }
ResolvedModuleConstPtr module;
+ QString name;
ScriptFunctionPtr prepareScript;
+ FileTags outputFileTags; // unused, if artifacts is non-empty
+ ScriptFunctionPtr outputArtifactsScript; // unused, if artifacts is non-empty
FileTags inputs;
FileTags auxiliaryInputs;
+ FileTags excludedAuxiliaryInputs;
FileTags usings;
FileTags explicitlyDependsOn;
bool multiplex;
- QList<RuleArtifactPtr> artifacts;
+ QList<RuleArtifactPtr> artifacts; // unused, if outputFileTags/outputArtifactsScript is non-empty
// members that we don't need to save
int ruleGraphId;
QString toString() const;
+ bool acceptsAsInput(Artifact *artifact) const;
FileTags staticOutputFileTags() const;
+ FileTags collectedOutputFileTags() const;
+ bool isDynamic() const;
private:
Rule() : multiplex(false), ruleGraphId(-1) {}
@@ -363,13 +374,25 @@ 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;
void setupBuildEnvironment(ScriptEngine *scriptEngine, const QProcessEnvironment &env) const;
void setupRunEnvironment(ScriptEngine *scriptEngine, const QProcessEnvironment &env) const;
- const QList<RuleConstPtr> &topSortedRules() const;
+ void registerAddedFileTag(const FileTag &fileTag, Artifact *artifact);
+ void registerAddedArtifact(Artifact *artifact);
+ void unregisterAddedArtifact(Artifact *artifact);
+ void registerRemovedFileTag(const FileTag &fileTag, Artifact *artifact);
+ void registerArtifactWithChangedInputs(Artifact *artifact);
+ void unregisterArtifactWithChangedInputs(Artifact *artifact);
+ void unmarkForReapplication(const RuleConstPtr &rule);
+ const ArtifactSet addedArtifactsByFileTag(const FileTag &tag) const;
+ const ArtifactSet removedArtifactsByFileTag(const FileTag &tag) const;
+ bool isMarkedForReapplication(const RuleConstPtr &rule) const;
+ ArtifactSet lookupArtifactsByFileTag(const FileTag &tag) const;
+
TopLevelProject *topLevelProject() const;
QStringList generatedFiles(const QString &baseFile, const FileTags &tags) const;
@@ -393,6 +416,8 @@ public:
QList<ResolvedProjectPtr> subProjects;
WeakPointer<ResolvedProject> parentProject;
+ void accept(BuildGraphVisitor *visitor) const;
+
void setProjectProperties(const QVariantMap &config) { m_projectProperties = config; }
const QVariantMap &projectProperties() const { return m_projectProperties; }
diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp
index a5d952efd..4dcec436d 100644
--- a/src/lib/corelib/language/projectresolver.cpp
+++ b/src/lib/corelib/language/projectresolver.cpp
@@ -576,26 +576,44 @@ void ProjectResolver::resolveRule(Item *item, ProjectContext *projectContext)
RulePtr rule = Rule::create();
// read artifacts
+ bool hasArtifactChildren = false;
bool hasAlwaysUpdatedArtifact = false;
foreach (Item *child, item->children()) {
if (Q_UNLIKELY(child->typeName() != QLatin1String("Artifact")))
throw ErrorInfo(Tr::tr("'Rule' can only have children of type 'Artifact'."),
child->location());
+ hasArtifactChildren = true;
resolveRuleArtifact(rule, child, &hasAlwaysUpdatedArtifact);
}
- if (Q_UNLIKELY(!hasAlwaysUpdatedArtifact))
+ if (Q_UNLIKELY(hasArtifactChildren && !hasAlwaysUpdatedArtifact))
throw ErrorInfo(Tr::tr("At least one output artifact of a rule "
"must have alwaysUpdated set to true."),
item->location());
+ rule->name = m_evaluator->stringValue(item, QLatin1String("name"));
rule->prepareScript = scriptFunctionValue(item, QLatin1String("prepare"));
+ rule->outputArtifactsScript = scriptFunctionValue(item, QLatin1String("outputArtifacts"));
+ if (rule->outputArtifactsScript->isValid()) {
+ if (hasArtifactChildren)
+ throw ErrorInfo(Tr::tr("The Rule.outputArtifacts script is not allowed in rules "
+ "that contain Artifact items."),
+ item->location());
+ rule->outputFileTags = m_evaluator->fileTagsValue(item, "outputFileTags");
+ if (rule->outputFileTags.isEmpty())
+ throw ErrorInfo(Tr::tr("Rule.outputFileTags must be specified if "
+ "Rule.outputArtifacts is specified."),
+ item->location());
+ }
+
rule->multiplex = m_evaluator->boolValue(item, QLatin1String("multiplex"));
rule->inputs = m_evaluator->fileTagsValue(item, QLatin1String("inputs"));
rule->usings = m_evaluator->fileTagsValue(item, QLatin1String("usings"));
rule->auxiliaryInputs
= m_evaluator->fileTagsValue(item, QLatin1String("auxiliaryInputs"));
+ rule->excludedAuxiliaryInputs
+ = m_evaluator->fileTagsValue(item, QLatin1String("excludedAuxiliaryInputs"));
rule->explicitlyDependsOn
= m_evaluator->fileTagsValue(item, QLatin1String("explicitlyDependsOn"));
rule->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule;
diff --git a/src/lib/corelib/tools/persistence.cpp b/src/lib/corelib/tools/persistence.cpp
index f9906ed01..cc751f889 100644
--- a/src/lib/corelib/tools/persistence.cpp
+++ b/src/lib/corelib/tools/persistence.cpp
@@ -40,7 +40,7 @@
namespace qbs {
namespace Internal {
-static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-61";
+static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-62";
PersistentPool::PersistentPool(const Logger &logger) : m_logger(logger)
{
diff --git a/src/lib/qtprofilesetup/templates/core.qbs b/src/lib/qtprofilesetup/templates/core.qbs
index 2b052d3d1..3199e0db5 100644
--- a/src/lib/qtprofilesetup/templates/core.qbs
+++ b/src/lib/qtprofilesetup/templates/core.qbs
@@ -203,50 +203,28 @@ Module {
}
Rule {
- inputs: ["moc_cpp"]
-
- Artifact {
- fileName: ModUtils.moduleProperty(product, "generatedFilesDir")
- + '/' + input.completeBaseName + ".moc"
- fileTags: ["hpp"]
- }
-
- prepare: {
- var cmd = new Command(Moc.fullPath(product),
- Moc.args(product, input, output.fileName));
- cmd.description = 'moc ' + FileInfo.fileName(input.fileName);
- cmd.highlight = 'codegen';
- return cmd;
- }
- }
-
- Rule {
- inputs: ["moc_hpp"]
-
- Artifact {
- fileName: ModUtils.moduleProperty(product, "generatedFilesDir")
- + "/moc_" + input.completeBaseName + ".cpp"
- fileTags: [ "cpp" ]
- }
-
- prepare: {
- var cmd = new Command(Moc.fullPath(product),
- Moc.args(product, input, output.fileName));
- cmd.description = 'moc ' + FileInfo.fileName(input.fileName);
- cmd.highlight = 'codegen';
- return cmd;
- }
- }
-
- Rule {
- inputs: ["moc_hpp_inc"]
-
- Artifact {
- fileName: ModUtils.moduleProperty(product, "generatedFilesDir")
- + "/moc_" + input.completeBaseName + ".cpp"
- fileTags: [ "hpp" ]
+ name: "QtCoreMocRule"
+ inputs: ["cpp", "hpp"]
+ auxiliaryInputs: ["qt_plugin_metadata"]
+ excludedAuxiliaryInputs: ["unmocable"]
+ outputFileTags: ["hpp", "cpp", "unmocable"]
+ outputArtifacts: {
+ var mocinfo = QtMocScanner.apply(input);
+ if (!mocinfo.hasQObjectMacro)
+ return [];
+ var artifact = { fileTags: ["unmocable"] };
+ if (input.fileTags.contains("hpp")) {
+ artifact.filePath = ModUtils.moduleProperty(product, "generatedFilesDir")
+ + "/moc_" + input.completeBaseName + ".cpp";
+ } else {
+ artifact.filePath = ModUtils.moduleProperty(product, "generatedFilesDir")
+ + '/' + input.completeBaseName + ".moc";
+ }
+ artifact.fileTags.push(mocinfo.mustCompile ? "cpp" : "hpp");
+ if (mocinfo.hasPluginMetaDataMacro)
+ artifact.explicitlyDependsOn = ["qt_plugin_metadata"];
+ return [artifact];
}
-
prepare: {
var cmd = new Command(Moc.fullPath(product),
Moc.args(product, input, output.fileName));
diff --git a/src/plugins/scanner/cpp/cppscanner.cpp b/src/plugins/scanner/cpp/cppscanner.cpp
index 2735aa378..4bee84dd3 100644
--- a/src/plugins/scanner/cpp/cppscanner.cpp
+++ b/src/plugins/scanner/cpp/cppscanner.cpp
@@ -257,7 +257,7 @@ static const char **additionalFileTags(void *opaq, int *size)
{
static const char *thMocCpp[] = { "moc_cpp" };
static const char *thMocHpp[] = { "moc_hpp" };
- static const char *thMocPluginHpp[] = { "moc_plugin_hpp" };
+ static const char *thMocPluginHpp[] = { "moc_hpp_plugin" };
Opaq *opaque = static_cast<Opaq*>(opaq);
if (opaque->hasQObjectMacro) {
diff --git a/tests/auto/api/tst_api.cpp b/tests/auto/api/tst_api.cpp
index 7536f4302..3ef5d7364 100644
--- a/tests/auto/api/tst_api.cpp
+++ b/tests/auto/api/tst_api.cpp
@@ -326,6 +326,15 @@ void TestApi::changeContent()
QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
}
+static qbs::ErrorInfo forceRuleEvaluation(const qbs::Project project)
+{
+ qbs::BuildOptions buildOptions;
+ buildOptions.setDryRun(true);
+ QScopedPointer<qbs::BuildJob> buildJob(project.buildAllProducts(buildOptions));
+ waitForFinished(buildJob.data());
+ return buildJob->error();
+}
+
void TestApi::disabledInstallGroup()
{
qbs::SetupProjectParameters setupParams = defaultSetupParameters();
@@ -335,7 +344,11 @@ void TestApi::disabledInstallGroup()
m_logSink, 0));
waitForFinished(job.data());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
- qbs::Project project = job->project();
+ const qbs::Project project = job->project();
+
+ const qbs::ErrorInfo errorInfo = forceRuleEvaluation(project);
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+
qbs::ProjectData projectData = project.projectData();
QCOMPARE(projectData.allProducts().count(), 1);
qbs::ProductData product = projectData.allProducts().first();
@@ -358,6 +371,10 @@ void TestApi::fileTagsFilterOverride()
waitForFinished(job.data());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
qbs::Project project = job->project();
+
+ const qbs::ErrorInfo errorInfo = forceRuleEvaluation(project);
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+
qbs::ProjectData projectData = project.projectData();
QCOMPARE(projectData.allProducts().count(), 1);
const qbs::ProductData product = projectData.allProducts().first();
@@ -378,6 +395,10 @@ void TestApi::installableFiles()
waitForFinished(job.data());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
qbs::Project project = job->project();
+
+ const qbs::ErrorInfo errorInfo = forceRuleEvaluation(project);
+ QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString()));
+
qbs::ProjectData projectData = project.projectData();
QCOMPARE(projectData.allProducts().count(), 1);
qbs::ProductData product = projectData.allProducts().first();
diff --git a/tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/main.cpp b/tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/main.cpp
new file mode 100644
index 000000000..940a7628d
--- /dev/null
+++ b/tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/main.cpp
@@ -0,0 +1,7 @@
+#include "object.h"
+
+int main()
+{
+ Object o;
+ o.f();
+}
diff --git a/tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/object.cpp b/tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/object.cpp
new file mode 100644
index 000000000..aab24f6c0
--- /dev/null
+++ b/tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/object.cpp
@@ -0,0 +1,13 @@
+#include "object.h"
+
+#include <QObject>
+
+// class InternalClass : public QObject
+// {
+// Q_OBJECT
+// };
+
+void Object::f() { }
+
+
+// #include "object.moc"
diff --git a/tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/object.h b/tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/object.h
new file mode 100644
index 000000000..37070b494
--- /dev/null
+++ b/tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/object.h
@@ -0,0 +1,4 @@
+class Object {
+public:
+ void f();
+};
diff --git a/tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/project.qbs b/tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/project.qbs
new file mode 100644
index 000000000..6c8db1913
--- /dev/null
+++ b/tests/auto/blackbox/testdata/add-qobject-macro-to-cpp-file/project.qbs
@@ -0,0 +1,7 @@
+import qbs
+
+CppApplication {
+ Depends { name: "Qt.core" }
+ files: ["main.cpp", "object.h", "object.cpp"]
+}
+
diff --git a/tests/auto/blackbox/testdata/added-file-persistent/file.cpp b/tests/auto/blackbox/testdata/added-file-persistent/file.cpp
new file mode 100644
index 000000000..8101b05dc
--- /dev/null
+++ b/tests/auto/blackbox/testdata/added-file-persistent/file.cpp
@@ -0,0 +1 @@
+void f() { }
diff --git a/tests/auto/blackbox/testdata/added-file-persistent/main.cpp b/tests/auto/blackbox/testdata/added-file-persistent/main.cpp
new file mode 100644
index 000000000..1921f1feb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/added-file-persistent/main.cpp
@@ -0,0 +1,3 @@
+void f();
+
+int main() { f(); }
diff --git a/tests/auto/blackbox/testdata/added-file-persistent/project.qbs b/tests/auto/blackbox/testdata/added-file-persistent/project.qbs
new file mode 100644
index 000000000..672886646
--- /dev/null
+++ b/tests/auto/blackbox/testdata/added-file-persistent/project.qbs
@@ -0,0 +1,8 @@
+import qbs
+
+CppApplication {
+ files: [
+ 'main.cpp',
+ /* 'file.cpp' */
+ ]
+}
diff --git a/tests/auto/blackbox/testdata/fileTagger/moc_cpp.qbs b/tests/auto/blackbox/testdata/fileTagger/moc_cpp.qbs
index b56412bed..2bc02e23c 100644
--- a/tests/auto/blackbox/testdata/fileTagger/moc_cpp.qbs
+++ b/tests/auto/blackbox/testdata/fileTagger/moc_cpp.qbs
@@ -20,7 +20,7 @@ Project {
Rule {
inputs: ['text']
Artifact {
- fileTags: ['cpp', 'moc_cpp']
+ fileTags: ['cpp']
fileName: input.baseName + '.cpp'
}
prepare: {
diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp
index d92896223..105af71a8 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -199,6 +199,63 @@ void TestBlackbox::initTestCase()
ccp(testSourceDir, testDataDir);
}
+void TestBlackbox::addedFilePersistent()
+{
+ QDir::setCurrent(testDataDir + QLatin1String("/added-file-persistent"));
+
+ // On the initial run, linking will fail.
+ QbsRunParameters failedRunParams;
+ failedRunParams.expectFailure = true;
+ QVERIFY(runQbs(failedRunParams) != 0);
+
+ // Add a file. qbs must schedule it for rule application on the next build.
+ QFile projectFile("project.qbs");
+ QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString()));
+ const QByteArray originalContent = projectFile.readAll();
+ QByteArray addedFileContent = originalContent;
+ addedFileContent.replace("/* 'file.cpp' */", "'file.cpp'");
+ projectFile.resize(0);
+ projectFile.write(addedFileContent);
+ waitForNewTimestamp();
+ projectFile.flush();
+ QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
+
+ // Remove the file again. qbs must unschedule the rule application again.
+ // Consequently, the linking step must fail as in the initial run.
+ projectFile.resize(0);
+ projectFile.write(originalContent);
+ waitForNewTimestamp();
+ projectFile.flush();
+ QVERIFY(runQbs(failedRunParams) != 0);
+
+ // Add the file again. qbs must schedule it for rule application on the next build.
+ projectFile.resize(0);
+ projectFile.write(addedFileContent);
+ waitForNewTimestamp();
+ projectFile.close();
+ QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
+
+ // qbs must remember that a file was scheduled for rule application. The build must then
+ // succeed, as now all necessary symbols are linked in.
+ QCOMPARE(runQbs(), 0);
+}
+
+void TestBlackbox::addQObjectMacroToCppFile()
+{
+ QDir::setCurrent(testDataDir + QLatin1String("/add-qobject-macro-to-cpp-file"));
+ QCOMPARE(runQbs(), 0);
+
+ waitForNewTimestamp();
+ QFile cppFile("object.cpp");
+ QVERIFY2(cppFile.open(QIODevice::ReadWrite), qPrintable(cppFile.errorString()));
+ QByteArray contents = cppFile.readAll();
+ contents.replace("// ", "");
+ cppFile.resize(0);
+ cppFile.write(contents);
+ cppFile.close();
+ QCOMPARE(runQbs(), 0);
+}
+
void TestBlackbox::baseProperties()
{
QDir::setCurrent(testDataDir + QLatin1String("/baseProperties"));
@@ -1331,22 +1388,6 @@ void TestBlackbox::propertyChanges()
QVERIFY(!m_qbsStdout.contains("compiling lib.cpp"));
QVERIFY(!m_qbsStdout.contains("generated.txt"));
QVERIFY(m_qbsStdout.contains("Making output from input"));
-
- // Incremental build, irrelevant file tag of a rule in a module changed.
- waitForNewTimestamp();
- QVERIFY(moduleFile.open(QIODevice::ReadWrite));
- contents = moduleFile.readAll();
- contents.replace("inputs: ['test-input']", "inputs: ['test-input', 'hupe']");
- moduleFile.resize(0);
- moduleFile.write(contents);
- moduleFile.close();
- QCOMPARE(runQbs(params), 0);
- QVERIFY(!m_qbsStdout.contains("compiling source1.cpp"));
- QVERIFY(!m_qbsStdout.contains("compiling source2.cpp"));
- QVERIFY(!m_qbsStdout.contains("compiling source3.cpp"));
- QVERIFY(!m_qbsStdout.contains("compiling lib.cpp"));
- QVERIFY(!m_qbsStdout.contains("generated.txt"));
- QVERIFY(!m_qbsStdout.contains("Making output from input"));
}
void TestBlackbox::disabledProduct()
@@ -1388,7 +1429,6 @@ void TestBlackbox::dynamicLibs()
void TestBlackbox::dynamicRuleOutputs()
{
- SKIP_TEST("QBS-370");
const QString testDir = testDataDir + "/dynamicRuleOutputs";
QDir::setCurrent(testDir);
if (QFile::exists("work"))
@@ -1585,6 +1625,30 @@ void TestBlackbox::inheritQbsSearchPaths()
QCOMPARE(runQbs(), 0);
}
+void TestBlackbox::mocCppIncluded()
+{
+ QDir::setCurrent(testDataDir + "/moc_hpp_included");
+ QCOMPARE(runQbs(), 0); // Initial build.
+
+ // Touch header and try again.
+ waitForNewTimestamp();
+ QFile headerFile("object.h");
+ QVERIFY2(headerFile.open(QIODevice::WriteOnly | QIODevice::Append),
+ qPrintable(headerFile.errorString()));
+ headerFile.write("\n");
+ headerFile.close();
+ QCOMPARE(runQbs(), 0);
+
+ // Touch cpp file and try again.
+ waitForNewTimestamp();
+ QFile cppFile("object.cpp");
+ QVERIFY2(cppFile.open(QIODevice::WriteOnly | QIODevice::Append),
+ qPrintable(cppFile.errorString()));
+ cppFile.write("\n");
+ cppFile.close();
+ QCOMPARE(runQbs(), 0);
+}
+
void TestBlackbox::objC()
{
QDir::setCurrent(testDataDir + "/objc");
diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h
index 0c516d497..b29b86c52 100644
--- a/tests/auto/blackbox/tst_blackbox.h
+++ b/tests/auto/blackbox/tst_blackbox.h
@@ -96,6 +96,8 @@ public slots:
void initTestCase();
private slots:
+ void addedFilePersistent();
+ void addQObjectMacroToCppFile();
void baseProperties();
void build_project_data();
void build_project();
@@ -117,6 +119,7 @@ private slots:
void jsExtensionsPropertyList();
void jsExtensionsTextFile();
void inheritQbsSearchPaths();
+ void mocCppIncluded();
void objC();
void properQuoting();
void propertiesBlocks();