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