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