summaryrefslogtreecommitdiff
path: root/src/lib/buildgraph/executor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/buildgraph/executor.cpp')
-rw-r--r--src/lib/buildgraph/executor.cpp351
1 files changed, 223 insertions, 128 deletions
diff --git a/src/lib/buildgraph/executor.cpp b/src/lib/buildgraph/executor.cpp
index 5d8ea52cf..fc80eff87 100644
--- a/src/lib/buildgraph/executor.cpp
+++ b/src/lib/buildgraph/executor.cpp
@@ -29,14 +29,16 @@
#include "executor.h"
#include "artifactvisitor.h"
-#include "automoc.h"
#include "buildgraph.h"
#include "productbuilddata.h"
#include "projectbuilddata.h"
#include "cycledetector.h"
#include "executorjob.h"
#include "inputartifactscanner.h"
+#include "phonytarget.h"
+#include "rulenode.h"
#include "rulesevaluationcontext.h"
+#include "tagscannerrunner.h"
#include <buildgraph/transformer.h>
#include <language/language.h>
@@ -91,7 +93,7 @@ private:
};
-bool Executor::ComparePriority::operator() (const Artifact *x, const Artifact *y) const
+bool Executor::ComparePriority::operator() (const BuildGraphNode *x, const BuildGraphNode *y) const
{
return x->product->buildData->buildPriority < y->product->buildData->buildPriority;
}
@@ -106,10 +108,6 @@ Executor::Executor(const Logger &logger, QObject *parent)
, m_doDebug(logger.debugEnabled())
{
m_inputArtifactScanContext = new InputArtifactScannerContext(&m_scanResultCache);
- m_autoMoc = new AutoMoc(logger);
- connect(m_autoMoc, SIGNAL(reportCommandDescription(QString,QString)),
- this, SIGNAL(reportCommandDescription(QString,QString)));
- m_autoMoc->setScanResultCache(&m_scanResultCache);
}
Executor::~Executor()
@@ -119,7 +117,6 @@ Executor::~Executor()
delete job;
foreach (ExecutorJob *job, m_processingJobs.keys())
delete job;
- delete m_autoMoc; // delete before shared scan result cache
delete m_inputArtifactScanContext;
}
@@ -225,6 +222,7 @@ void Executor::doBuild()
}
QBS_CHECK(m_state == ExecutorIdle);
m_leaves = Leaves();
+ m_changedSourceArtifacts.clear();
m_error.clear();
m_explicitlyCanceled = false;
m_activeFileTags = FileTags::fromStringList(m_buildOptions.activeFileTags());
@@ -250,8 +248,7 @@ void Executor::doBuild()
foreach (ExecutorJob * const job, m_availableJobs)
job->setDryRun(m_buildOptions.dryRun());
- bool sourceFilesChanged = false;
- prepareAllArtifacts(&sourceFilesChanged);
+ prepareAllNodes();
Artifact::BuildState initialBuildState = m_buildOptions.changedFiles().isEmpty()
? Artifact::Buildable : Artifact::Built;
@@ -281,19 +278,22 @@ void Executor::doBuild()
// find the root nodes
m_roots.clear();
foreach (const ResolvedProductPtr &product, m_productsToBuild) {
- foreach (Artifact *targetArtifact, product->buildData->targetArtifacts) {
- m_roots += targetArtifact;
-
- // The user expects that he can delete target artifacts and they get rebuilt.
- // To achieve this we must retrieve their timestamps.
- targetArtifact->setTimestamp(FileInfo(targetArtifact->filePath()).lastModified());
+ foreach (BuildGraphNode *rootNode, product->buildData->roots) {
+ m_roots += rootNode;
+ Artifact *targetArtifact = dynamic_cast<Artifact *>(rootNode);
+ if (targetArtifact && product->buildData->targetArtifacts.contains(targetArtifact)) {
+ // The user expects that he can delete target artifacts and they get rebuilt.
+ // To achieve this we must retrieve their timestamps.
+ targetArtifact->setTimestamp(FileInfo(targetArtifact->filePath()).lastModified());
+ }
}
}
prepareReachableArtifacts(initialBuildState);
- setupProgressObserver(sourceFilesChanged);
- if (sourceFilesChanged)
- runAutoMoc();
+ setupProgressObserver(!m_changedSourceArtifacts.isEmpty());
+ if (!m_changedSourceArtifacts.isEmpty()) {
+ runTagScanners();
+ }
initLeaves(changedArtifacts);
if (!scheduleJobs()) {
m_logger.qbsTrace() << "Nothing to do at all, finishing.";
@@ -306,48 +306,49 @@ void Executor::setBuildOptions(const BuildOptions &buildOptions)
m_buildOptions = buildOptions;
}
-static void initArtifactsBottomUp(Artifact *artifact)
+static void initArtifactsBottomUp(BuildGraphNode *node)
{
- if (artifact->buildState == Artifact::Untouched)
+ if (node->buildState == Artifact::Untouched)
return;
- artifact->buildState = Artifact::Buildable;
- foreach (Artifact *parent, artifact->parents)
+ node->buildState = Artifact::Buildable;
+ foreach (BuildGraphNode *parent, node->parents)
initArtifactsBottomUp(parent);
}
void Executor::initLeaves(const QList<Artifact *> &changedArtifacts)
{
if (changedArtifacts.isEmpty()) {
- QSet<Artifact *> seenArtifacts;
- foreach (Artifact *root, m_roots)
- initLeavesTopDown(root, seenArtifacts);
+ QSet<BuildGraphNode *> seenNodes;
+ foreach (BuildGraphNode *root, m_roots)
+ initLeavesTopDown(root, seenNodes);
} else {
- foreach (Artifact *artifact, changedArtifacts) {
+ foreach (BuildGraphNode *artifact, changedArtifacts) {
m_leaves.push(artifact);
initArtifactsBottomUp(artifact);
}
}
}
-void Executor::initLeavesTopDown(Artifact *artifact, QSet<Artifact *> &seenArtifacts)
+void Executor::initLeavesTopDown(BuildGraphNode *node, QSet<BuildGraphNode *> &seenNodes)
{
- if (seenArtifacts.contains(artifact))
+ if (seenNodes.contains(node))
return;
- seenArtifacts += artifact;
+ seenNodes += node;
// Artifacts that appear in the build graph after
// prepareBuildGraph() has been called, must be initialized.
- if (artifact->buildState == Artifact::Untouched) {
- artifact->buildState = Artifact::Buildable;
- if (artifact->artifactType == Artifact::SourceFile)
+ if (node->buildState == Artifact::Untouched) {
+ node->buildState = Artifact::Buildable;
+ Artifact *artifact = dynamic_cast<Artifact *>(node);
+ if (artifact && artifact->artifactType == Artifact::SourceFile)
retrieveSourceFileTimestamp(artifact);
}
- if (artifact->children.isEmpty()) {
- m_leaves.push(artifact);
+ if (node->children.isEmpty()) {
+ m_leaves.push(node);
} else {
- foreach (Artifact *child, artifact->children)
- initLeavesTopDown(child, seenArtifacts);
+ foreach (BuildGraphNode *child, node->children)
+ initLeavesTopDown(child, seenNodes);
}
}
@@ -356,11 +357,34 @@ bool Executor::scheduleJobs()
{
QBS_CHECK(m_state == ExecutorRunning);
while (!m_leaves.empty() && !m_availableJobs.isEmpty()) {
- Artifact * const artifact = m_leaves.top();
+ BuildGraphNode * const nodeToBuild = m_leaves.top();
m_leaves.pop();
- buildArtifact(artifact);
+
+ switch (nodeToBuild->buildState) {
+ case BuildGraphNode::Untouched:
+ QBS_ASSERT(!"untouched node in leaves list", /* ignore */);
+ continue;
+ case BuildGraphNode::Buildable:
+ // This is the only state in which we want to build a node.
+ break;
+ case BuildGraphNode::Building:
+ if (m_doDebug) {
+ m_logger.qbsDebug() << "[EXEC] " << nodeToBuild->toString();
+ m_logger.qbsDebug() << "[EXEC] node is currently being built. Skipping.";
+ }
+ continue;
+ case BuildGraphNode::Built:
+ if (m_doDebug) {
+ m_logger.qbsDebug() << "[EXEC] " << nodeToBuild->toString();
+ m_logger.qbsDebug() << "[EXEC] node already built. Skipping.";
+ }
+ continue;
+ }
+
+ nodeToBuild->accept(this);
}
return !m_leaves.empty() || !m_processingJobs.isEmpty();
+
}
bool Executor::isUpToDate(Artifact *artifact) const
@@ -387,11 +411,15 @@ bool Executor::isUpToDate(Artifact *artifact) const
return false;
}
- foreach (Artifact *child, artifact->children) {
- QBS_CHECK(child->timestamp().isValid());
+ foreach (BuildGraphNode *child, artifact->children) {
+ Artifact *childArtifact = dynamic_cast<Artifact *>(child);
+ if (!childArtifact)
+ continue;
+ QBS_CHECK(childArtifact->timestamp().isValid());
if (debug)
- m_logger.qbsDebug() << "[UTD] child timestamp " << child->timestamp().toString();
- if (artifact->timestamp() < child->timestamp())
+ m_logger.qbsDebug() << "[UTD] child timestamp "
+ << childArtifact->timestamp().toString();
+ if (artifact->timestamp() < childArtifact->timestamp())
return false;
}
@@ -429,13 +457,6 @@ void Executor::buildArtifact(Artifact *artifact)
if (m_doDebug)
m_logger.qbsDebug() << "[EXEC] " << relativeArtifactFileName(artifact);
- // Skip artifacts that are already built.
- if (artifact->buildState == Artifact::Built) {
- if (m_doDebug)
- m_logger.qbsDebug() << "[EXEC] artifact already built. Skipping.";
- return;
- }
-
// skip artifacts without transformer
if (artifact->artifactType != Artifact::Generated) {
// For source artifacts, that were not reachable when initializing the build, we must
@@ -495,7 +516,7 @@ void Executor::buildArtifact(Artifact *artifact)
// create the output directories
if (!m_buildOptions.dryRun()) {
- ArtifactList::const_iterator it = artifact->transformer->outputs.begin();
+ ArtifactSet::const_iterator it = artifact->transformer->outputs.begin();
for (; it != artifact->transformer->outputs.end(); ++it) {
Artifact *output = *it;
QDir outDir = QFileInfo(output->filePath()).absoluteDir();
@@ -511,8 +532,8 @@ void Executor::buildArtifact(Artifact *artifact)
// postpone the build of this artifact, if new dependencies found
if (scanner.newDependencyAdded()) {
bool buildingDependenciesFound = false;
- QVector<Artifact *> unbuiltDependencies;
- foreach (Artifact *dependency, artifact->children) {
+ QVector<BuildGraphNode *> unbuiltDependencies;
+ foreach (BuildGraphNode *dependency, artifact->children) {
switch (dependency->buildState) {
case Artifact::Untouched:
case Artifact::Buildable:
@@ -548,6 +569,59 @@ void Executor::buildArtifact(Artifact *artifact)
job->run(artifact->transformer.data(), artifact->product);
}
+void Executor::buildPhonyTarget(PhonyTarget *target)
+{
+ if (target->transformer()) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] " << target->toString();
+
+ // Skip targets that are already built.
+ if (target->buildState == Artifact::Built) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] phony target already built. Skipping.";
+ return;
+ }
+
+ } else {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] " << target->toString()
+ << " No transformer. Skipping.";
+ }
+ finishNode(target);
+}
+
+void Executor::executeRuleNode(RuleNode *ruleNode)
+{
+ RuleNode::ApplicationResult result;
+ ruleNode->apply(m_logger, &result);
+
+ if (result.upToDate) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] " << ruleNode->toString()
+ << " is up to date. Skipping.";
+ } else {
+ qDebug() << "EXEC RULE" << ruleNode->toString();
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] " << ruleNode->toString();
+ const QVector<BuildGraphNode *> &createdNodes = result.createdNodes;
+ const WeakPointer<ResolvedProduct> &product = ruleNode->product;
+ foreach (BuildGraphNode *node, createdNodes) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] rule created " << node->toString();
+ loggedConnect(node, ruleNode, m_logger);
+ Artifact *artifact = dynamic_cast<Artifact *>(node);
+ if (!artifact)
+ continue;
+ if (artifact->fileTags.matches(product->fileTags)) {
+ product->buildData->roots += artifact;
+ product->buildData->targetArtifacts += artifact;
+ }
+ }
+ insertLeavesAfterAddingDependencies(createdNodes);
+ }
+ finishNode(ruleNode);
+}
+
void Executor::finishJob(ExecutorJob *job, bool success)
{
QBS_CHECK(job);
@@ -583,27 +657,21 @@ void Executor::finishJob(ExecutorJob *job, bool success)
}
}
-static bool allChildrenBuilt(Artifact *artifact)
+static bool allChildrenBuilt(BuildGraphNode *node)
{
- foreach (Artifact *child, artifact->children)
+ foreach (BuildGraphNode *child, node->children)
if (child->buildState != Artifact::Built)
return false;
return true;
}
-void Executor::finishArtifact(Artifact *leaf)
+void Executor::finishNode(BuildGraphNode *leaf)
{
- QBS_CHECK(leaf);
-
- if (m_doTrace)
- m_logger.qbsTrace() << "[EXEC] finishArtifact " << relativeArtifactFileName(leaf);
-
leaf->buildState = Artifact::Built;
- m_scanResultCache.remove(leaf->filePath());
- foreach (Artifact *parent, leaf->parents) {
+ foreach (BuildGraphNode *parent, leaf->parents) {
if (parent->buildState != Artifact::Buildable) {
if (m_doTrace) {
- m_logger.qbsTrace() << "[EXEC] parent " << relativeArtifactFileName(parent)
+ m_logger.qbsTrace() << "[EXEC] parent " << parent->toString()
<< " build state: " << toString(parent->buildState);
}
continue;
@@ -612,16 +680,26 @@ void Executor::finishArtifact(Artifact *leaf)
if (allChildrenBuilt(parent)) {
m_leaves.push(parent);
if (m_doTrace) {
- m_logger.qbsTrace() << "[EXEC] finishArtifact adds leaf "
- << relativeArtifactFileName(parent) << " " << toString(parent->buildState);
+ m_logger.qbsTrace() << "[EXEC] finishNode adds leaf "
+ << parent->toString() << " " << toString(parent->buildState);
}
} else {
if (m_doTrace) {
- m_logger.qbsTrace() << "[EXEC] parent " << relativeArtifactFileName(parent)
+ m_logger.qbsTrace() << "[EXEC] parent " << parent->toString()
<< " build state: " << toString(parent->buildState);
}
}
}
+}
+
+void Executor::finishArtifact(Artifact *leaf)
+{
+ QBS_CHECK(leaf);
+ if (m_doTrace)
+ m_logger.qbsTrace() << "[EXEC] finishArtifact " << relativeArtifactFileName(leaf);
+
+ finishNode(leaf);
+ m_scanResultCache.remove(leaf->filePath());
if (leaf->transformer)
foreach (Artifact *sideBySideArtifact, leaf->transformer->outputs)
@@ -632,28 +710,28 @@ void Executor::finishArtifact(Artifact *leaf)
m_progressObserver->incrementProgressValue(BuildEffortCalculator::multiplier(leaf));
}
-void Executor::insertLeavesAfterAddingDependencies_recurse(Artifact *const artifact,
- QSet<Artifact *> *seenArtifacts, Leaves *leaves) const
+void Executor::insertLeavesAfterAddingDependencies_recurse(BuildGraphNode *const node,
+ QSet<BuildGraphNode *> *seenNodes, Leaves *leaves) const
{
- if (seenArtifacts->contains(artifact))
+ if (seenNodes->contains(node))
return;
- seenArtifacts->insert(artifact);
+ seenNodes->insert(node);
- if (artifact->buildState == Artifact::Untouched)
- artifact->buildState = Artifact::Buildable;
+ if (node->buildState == Artifact::Untouched)
+ node->buildState = Artifact::Buildable;
bool isLeaf = true;
- foreach (Artifact *child, artifact->children) {
+ foreach (BuildGraphNode *child, node->children) {
if (child->buildState != Artifact::Built) {
isLeaf = false;
- insertLeavesAfterAddingDependencies_recurse(child, seenArtifacts, leaves);
+ insertLeavesAfterAddingDependencies_recurse(child, seenNodes, leaves);
}
}
if (isLeaf) {
if (m_doDebug)
- m_logger.qbsDebug() << "[EXEC] adding leaf " << relativeArtifactFileName(artifact);
- leaves->push(artifact);
+ m_logger.qbsDebug() << "[EXEC] adding leaf " << node->toString();
+ leaves->push(node);
}
}
@@ -662,11 +740,11 @@ QString Executor::configString() const
return tr(" for configuration %1").arg(m_project->id());
}
-void Executor::insertLeavesAfterAddingDependencies(QVector<Artifact *> dependencies)
+void Executor::insertLeavesAfterAddingDependencies(QVector<BuildGraphNode *> dependencies)
{
- QSet<Artifact *> seenArtifacts;
- foreach (Artifact *dependency, dependencies)
- insertLeavesAfterAddingDependencies_recurse(dependency, &seenArtifacts, &m_leaves);
+ QSet<BuildGraphNode *> seenNodes;
+ foreach (BuildGraphNode *dependency, dependencies)
+ insertLeavesAfterAddingDependencies_recurse(dependency, &seenNodes, &m_leaves);
}
void Executor::cancelJobs()
@@ -731,27 +809,19 @@ void Executor::addExecutorJobs(int jobNumber)
}
}
-void Executor::runAutoMoc()
+void Executor::runTagScanners()
{
- bool autoMocApplied = false;
foreach (const ResolvedProductPtr &product, m_productsToBuild) {
if (m_progressObserver && m_progressObserver->canceled())
throw ErrorInfo(Tr::tr("Build canceled%1.").arg(configString()));
- // HACK call the automoc thingy here only if we have use Qt/core module
- foreach (const ResolvedModuleConstPtr &m, product->modules) {
- if (m->name == "Qt/core") {
- autoMocApplied = true;
- m_autoMoc->apply(product);
- break;
- }
- }
+ QHash<FileTag, QList<Artifact *> > sourceArtifacts;
+ foreach (Artifact *artifact, m_changedSourceArtifacts)
+ foreach (const FileTag &tag, artifact->fileTags)
+ sourceArtifacts[tag] += artifact;
+ foreach (const TagScannerConstPtr &rootTagScanner, product->tagScanners)
+ runTagScannersTopOrdered(&m_scanResultCache, product, rootTagScanner, sourceArtifacts,
+ m_logger);
}
- if (autoMocApplied) {
- foreach (const ResolvedProductConstPtr &product, m_productsToBuild)
- CycleDetector(m_logger).visitProduct(product);
- }
- if (m_progressObserver)
- m_progressObserver->incrementProgressValue(m_mocEffort);
}
void Executor::onProcessError(const qbs::ErrorInfo &err)
@@ -800,8 +870,8 @@ void Executor::finish()
QStringList unbuiltProductNames;
foreach (const ResolvedProductPtr &product, m_productsToBuild) {
- foreach (Artifact *artifact, product->buildData->targetArtifacts) {
- if (artifact->buildState != Artifact::Built) {
+ foreach (BuildGraphNode *rootNode, product->buildData->roots) {
+ if (rootNode->buildState != Artifact::Built) {
unbuiltProductNames += product->name;
break;
}
@@ -823,35 +893,59 @@ void Executor::finish()
emit finished();
}
+bool Executor::visit(Artifact *artifact)
+{
+ buildArtifact(artifact);
+ return false;
+}
+
+bool Executor::visit(PhonyTarget *target)
+{
+ buildPhonyTarget(target);
+ return false;
+}
+
+bool Executor::visit(RuleNode *ruleNode)
+{
+ executeRuleNode(ruleNode);
+ return false;
+}
+
/**
* Sets the state of all artifacts in the graph to "untouched".
* This must be done before doing a build.
*
* Retrieves the timestamps of source artifacts.
*
- * This function sets *sourceFilesChanged to true, if the timestamp of a reachable source artifact
- * changed.
+ * This function also fills the list of changed source files.
*/
-void Executor::prepareAllArtifacts(bool *sourceFilesChanged)
+void Executor::prepareAllNodes()
{
foreach (const ResolvedProductPtr &product, m_productsToBuild) {
- foreach (Artifact *artifact, product->buildData->artifacts) {
- artifact->buildState = Artifact::Untouched;
- artifact->inputsScanned = false;
- artifact->timestampRetrieved = false;
+ foreach (BuildGraphNode *node, product->buildData->nodes) {
+ node->buildState = Artifact::Untouched;
+ Artifact *artifact = dynamic_cast<Artifact *>(node);
+ if (artifact)
+ prepareArtifact(artifact);
+ }
+ }
+}
- if (artifact->artifactType == Artifact::SourceFile) {
- const FileTime oldTimestamp = artifact->timestamp();
- retrieveSourceFileTimestamp(artifact);
- if (oldTimestamp != artifact->timestamp())
- *sourceFilesChanged = true;
- }
+void Executor::prepareArtifact(Artifact *artifact)
+{
+ artifact->inputsScanned = false;
+ artifact->timestampRetrieved = false;
- // Timestamps of file dependencies must be invalid for every build.
- foreach (FileDependency *fileDependency, artifact->fileDependencies)
- fileDependency->clearTimestamp();
- }
+ if (artifact->artifactType == Artifact::SourceFile) {
+ const FileTime oldTimestamp = artifact->timestamp();
+ retrieveSourceFileTimestamp(artifact);
+ if (oldTimestamp != artifact->timestamp())
+ m_changedSourceArtifacts.append(artifact);
}
+
+ // Timestamps of file dependencies must be invalid for every build.
+ foreach (FileDependency *fileDependency, artifact->fileDependencies)
+ fileDependency->clearTimestamp();
}
/**
@@ -860,38 +954,39 @@ void Executor::prepareAllArtifacts(bool *sourceFilesChanged)
*/
void Executor::prepareReachableArtifacts(const Artifact::BuildState buildState)
{
- foreach (Artifact *root, m_roots)
+ foreach (BuildGraphNode *root, m_roots)
prepareReachableArtifacts_impl(root, buildState);
}
-void Executor::prepareReachableArtifacts_impl(Artifact *artifact,
+void Executor::prepareReachableArtifacts_impl(BuildGraphNode *node,
const Artifact::BuildState buildState)
{
- if (artifact->buildState != Artifact::Untouched)
+ if (node->buildState != Artifact::Untouched)
return;
- artifact->buildState = buildState;
- foreach (Artifact *child, artifact->children)
+ node->buildState = buildState;
+ foreach (BuildGraphNode *child, node->children)
prepareReachableArtifacts_impl(child, buildState);
}
void Executor::updateBuildGraph(Artifact::BuildState buildState)
{
- QSet<Artifact *> seenArtifacts;
- foreach (Artifact *root, m_roots)
+ QSet<BuildGraphNode *> seenArtifacts;
+ foreach (BuildGraphNode *root, m_roots)
updateBuildGraph_impl(root, buildState, seenArtifacts);
}
-void Executor::updateBuildGraph_impl(Artifact *artifact, Artifact::BuildState buildState, QSet<Artifact *> &seenArtifacts)
+void Executor::updateBuildGraph_impl(BuildGraphNode *node, Artifact::BuildState buildState,
+ QSet<BuildGraphNode *> &seenNodes)
{
- if (seenArtifacts.contains(artifact))
+ if (seenNodes.contains(node))
return;
- seenArtifacts += artifact;
- artifact->buildState = buildState;
+ seenNodes += node;
+ node->buildState = buildState;
- foreach (Artifact *child, artifact->children)
- updateBuildGraph_impl(child, buildState, seenArtifacts);
+ foreach (BuildGraphNode *child, node->children)
+ updateBuildGraph_impl(child, buildState, seenNodes);
}
void Executor::setState(ExecutorState s)