diff options
author | Christian Kandeler <christian.kandeler@digia.com> | 2014-01-09 17:50:40 +0100 |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@digia.com> | 2014-01-10 18:11:22 +0100 |
commit | 81af9acaa295a574c1cb5e6714725197dac7f530 (patch) | |
tree | cc8c94467f49a7d267e5249f624874feecc7eed4 /src/lib/corelib/buildgraph/projectbuilddata.cpp | |
parent | 2fe25eb3f20ffb4e58cb559f2bcb9950c963290a (diff) |
Move Qt profile setup into a dedicated library.
Otherwise all changes to the implementation will have to be duplicated
in IDEs.
Change-Id: I61e6d4fa1ee9b724eb5d9de9f233dc915a6c8bc3
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
Diffstat (limited to 'src/lib/corelib/buildgraph/projectbuilddata.cpp')
-rw-r--r-- | src/lib/corelib/buildgraph/projectbuilddata.cpp | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/src/lib/corelib/buildgraph/projectbuilddata.cpp b/src/lib/corelib/buildgraph/projectbuilddata.cpp new file mode 100644 index 000000000..1ba53abf2 --- /dev/null +++ b/src/lib/corelib/buildgraph/projectbuilddata.cpp @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Build Suite. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ +#include "projectbuilddata.h" + +#include "artifact.h" +#include "buildgraph.h" +#include "productbuilddata.h" +#include "command.h" +#include "rulesapplicator.h" +#include "rulesevaluationcontext.h" +#include "transformer.h" +#include <language/language.h> +#include <language/preparescriptobserver.h> +#include <language/scriptengine.h> +#include <logging/translator.h> +#include <tools/error.h> +#include <tools/persistence.h> +#include <tools/qbsassert.h> + +namespace qbs { +namespace Internal { + +ProjectBuildData::ProjectBuildData(const ProjectBuildData *other) + : isDirty(true), m_doCleanupInDestructor(true) +{ + // This is needed for temporary duplication of build data when doing change tracking. + if (other) { + *this = *other; + m_doCleanupInDestructor = false; + } +} + +ProjectBuildData::~ProjectBuildData() +{ + if (!m_doCleanupInDestructor) + return; + qDeleteAll(fileDependencies); +} + +QString ProjectBuildData::deriveBuildGraphFilePath(const QString &buildDir, const QString &projectId) +{ + return buildDir + QLatin1Char('/') + projectId + QLatin1String(".bg"); +} + +void ProjectBuildData::insertIntoLookupTable(FileResourceBase *fileres) +{ + QList<FileResourceBase *> &lst + = m_artifactLookupTable[fileres->fileName()][fileres->dirPath()]; + if (!lst.contains(fileres)) + lst.append(fileres); +} + +void ProjectBuildData::removeFromLookupTable(FileResourceBase *fileres) +{ + m_artifactLookupTable[fileres->fileName()][fileres->dirPath()].removeOne(fileres); +} + +QList<FileResourceBase *> ProjectBuildData::lookupFiles(const QString &filePath) const +{ + QString dirPath, fileName; + FileInfo::splitIntoDirectoryAndFileName(filePath, &dirPath, &fileName); + return lookupFiles(dirPath, fileName); +} + +QList<FileResourceBase *> ProjectBuildData::lookupFiles(const QString &dirPath, + const QString &fileName) const +{ + return m_artifactLookupTable.value(fileName).value(dirPath); +} + +QList<FileResourceBase *> ProjectBuildData::lookupFiles(const Artifact *artifact) const +{ + return lookupFiles(artifact->dirPath(), artifact->fileName()); +} + +void ProjectBuildData::insertFileDependency(FileDependency *dependency) +{ + fileDependencies += dependency; + insertIntoLookupTable(dependency); +} + +static void disconnectArtifactChildren(Artifact *artifact, const Logger &logger) +{ + if (logger.traceEnabled()) { + logger.qbsTrace() << QString::fromLocal8Bit("[BG] disconnectChildren: '%1'") + .arg(relativeArtifactFileName(artifact)); + } + foreach (Artifact * const child, artifact->children) + child->parents.remove(artifact); + artifact->children.clear(); + artifact->childrenAddedByScanner.clear(); +} + +static void disconnectArtifactParents(Artifact *artifact, ProjectBuildData *projectBuildData, + const Logger &logger) +{ + if (logger.traceEnabled()) { + logger.qbsTrace() << QString::fromLocal8Bit("[BG] disconnectParents: '%1'") + .arg(relativeArtifactFileName(artifact)); + } + foreach (Artifact * const parent, artifact->parents) { + parent->children.remove(artifact); + parent->childrenAddedByScanner.remove(artifact); + if (parent->transformer) { + parent->transformer->inputs.remove(artifact); + projectBuildData->artifactsThatMustGetNewTransformers += parent; + } + } + + artifact->parents.clear(); +} + +static void disconnectArtifact(Artifact *artifact, ProjectBuildData *projectBuildData, + const Logger &logger) +{ + disconnectArtifactChildren(artifact, logger); + disconnectArtifactParents(artifact, projectBuildData, logger); +} + +/*! + * 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 ProjectBuildData::removeArtifactAndExclusiveDependents(Artifact *artifact, + const Logger &logger, bool removeFromProduct, + ArtifactList *removedArtifacts) +{ + if (removedArtifacts) + removedArtifacts->insert(artifact); + foreach (Artifact *parent, artifact->parents) { + bool removeParent = false; + disconnect(parent, artifact, logger); + if (parent->children.isEmpty()) { + removeParent = true; + } else if (parent->transformer) { + artifactsThatMustGetNewTransformers += parent; + parent->transformer->inputs.remove(artifact); + removeParent = parent->transformer->inputs.isEmpty(); + } + if (removeParent) { + removeArtifactAndExclusiveDependents(parent, logger, removeFromProduct, + removedArtifacts); + } + } + const bool removeFromDisk = artifact->artifactType == Artifact::Generated; + removeArtifact(artifact, logger, removeFromDisk, removeFromProduct); +} + +void ProjectBuildData::removeArtifact(Artifact *artifact, + const Logger &logger, bool removeFromDisk, bool removeFromProduct) +{ + if (logger.traceEnabled()) + logger.qbsTrace() << "[BG] remove artifact " << relativeArtifactFileName(artifact); + + if (removeFromDisk) + removeGeneratedArtifactFromDisk(artifact, logger); + removeFromLookupTable(artifact); + if (removeFromProduct) { + artifact->product->buildData->artifacts.remove(artifact); + artifact->product->buildData->targetArtifacts.remove(artifact); + } + disconnectArtifact(artifact, this, logger); + artifactsThatMustGetNewTransformers -= artifact; + isDirty = true; +} + +void ProjectBuildData::updateNodesThatMustGetNewTransformer(const Logger &logger) +{ + RulesEvaluationContext::Scope s(evaluationContext.data()); + foreach (Artifact *artifact, artifactsThatMustGetNewTransformers) + updateNodeThatMustGetNewTransformer(artifact, logger); + artifactsThatMustGetNewTransformers.clear(); +} + +void ProjectBuildData::updateNodeThatMustGetNewTransformer(Artifact *artifact, const Logger &logger) +{ + QBS_CHECK(artifact->transformer); + + if (logger.debugEnabled()) { + logger.qbsDebug() << "[BG] updating transformer for " + << relativeArtifactFileName(artifact); + } + + removeGeneratedArtifactFromDisk(artifact, logger); + artifact->autoMocTimestamp.clear(); + artifact->clearTimestamp(); + + const RuleConstPtr rule = artifact->transformer->rule; + isDirty = true; + + QBS_CHECK(artifact->transformer); + foreach (Artifact * const sibling, artifact->transformer->outputs) + sibling->transformer.clear(); + + ArtifactsPerFileTagMap artifactsPerFileTag; + foreach (Artifact *input, artifact->children) { + foreach (const FileTag &fileTag, input->fileTags) + artifactsPerFileTag[fileTag] += input; + } + RulesApplicator rulesApplier(artifact->product, artifactsPerFileTag, logger); + rulesApplier.applyRule(rule); +} + +void ProjectBuildData::load(PersistentPool &pool) +{ + int count; + pool.stream() >> count; + fileDependencies.clear(); + fileDependencies.reserve(count); + for (; --count >= 0;) { + FileDependency *fileDependency = pool.idLoad<FileDependency>(); + insertFileDependency(fileDependency); + } +} + +void ProjectBuildData::store(PersistentPool &pool) const +{ + pool.storeContainer(fileDependencies); +} + + +BuildDataResolver::BuildDataResolver(const Logger &logger) : m_logger(logger) +{ +} + +void BuildDataResolver::resolveBuildData(const TopLevelProjectPtr &resolvedProject, + const RulesEvaluationContextPtr &evalContext) +{ + QBS_CHECK(!resolvedProject->buildData); + m_project = resolvedProject; + resolvedProject->buildData.reset(new ProjectBuildData); + resolvedProject->buildData->evaluationContext = evalContext; + const QList<ResolvedProductPtr> allProducts = resolvedProject->allProducts(); + evalContext->initializeObserver(Tr::tr("Setting up build graph for configuration %1") + .arg(resolvedProject->id()), allProducts.count() + 1); + foreach (ResolvedProductPtr rProduct, allProducts) { + if (rProduct->enabled) + resolveProductBuildData(rProduct); + evalContext->incrementProgressValue(); + } + evalContext->incrementProgressValue(); + doSanityChecks(resolvedProject, m_logger); +} + +void BuildDataResolver::resolveProductBuildDataForExistingProject(const TopLevelProjectPtr &project, + const QList<ResolvedProductPtr> &freshProducts) +{ + m_project = project; + foreach (const ResolvedProductPtr &product, freshProducts) { + if (product->enabled) + resolveProductBuildData(product); + } +} + +void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &product) +{ + if (product->buildData) + return; + + evalContext()->checkForCancelation(); + + product->buildData.reset(new ProductBuildData); + ArtifactsPerFileTagMap artifactsPerFileTag; + + foreach (ResolvedProductPtr dependency, product->dependencies) { + if (Q_UNLIKELY(!dependency->enabled)) { + QString msg = Tr::tr("Product '%1' depends on '%2' but '%2' is disabled."); + throw ErrorInfo(msg.arg(product->name, dependency->name)); + } + resolveProductBuildData(dependency); + } + + //add qbsFile artifact + Artifact *qbsFileArtifact = lookupArtifact(product, product->location.fileName()); + if (!qbsFileArtifact) { + qbsFileArtifact = new Artifact; + qbsFileArtifact->artifactType = Artifact::SourceFile; + qbsFileArtifact->setFilePath(product->location.fileName()); + qbsFileArtifact->properties = product->properties; + insertArtifact(product, qbsFileArtifact, m_logger); + } + qbsFileArtifact->fileTags.insert("qbs"); + artifactsPerFileTag["qbs"].insert(qbsFileArtifact); + + // read sources + foreach (const SourceArtifactConstPtr &sourceArtifact, product->allEnabledFiles()) { + QString filePath = sourceArtifact->absoluteFilePath; + if (lookupArtifact(product, filePath)) + continue; // ignore duplicate artifacts + + Artifact *artifact = createArtifact(product, sourceArtifact, m_logger); + foreach (const FileTag &fileTag, artifact->fileTags) + artifactsPerFileTag[fileTag].insert(artifact); + } + + // read manually added transformers + typedef QPair<ResolvedTransformerConstPtr, TransformerConstPtr> TrafoPair; + QList<TrafoPair> trafos; + foreach (const ResolvedTransformerConstPtr &rtrafo, product->transformers) { + ArtifactList inputArtifacts; + foreach (const QString &inputFileName, rtrafo->inputs) { + Artifact *artifact = lookupArtifact(product, inputFileName); + if (Q_UNLIKELY(!artifact)) + throw ErrorInfo(QString("Can't find artifact '%0' in the list of source files.").arg(inputFileName)); + inputArtifacts += artifact; + } + TransformerPtr transformer = Transformer::create(); + trafos += TrafoPair(rtrafo, transformer); + transformer->inputs = inputArtifacts; + const RulePtr rule = Rule::create(); + ResolvedModulePtr module = ResolvedModule::create(); + module->name = rtrafo->module->name; + rule->module = module; + rule->script = rtrafo->transform; + foreach (const SourceArtifactConstPtr &sourceArtifact, rtrafo->outputs) { + Artifact *outputArtifact = createArtifact(product, sourceArtifact, m_logger); + outputArtifact->artifactType = Artifact::Generated; + outputArtifact->transformer = transformer; + transformer->outputs += outputArtifact; + product->buildData->targetArtifacts += outputArtifact; + foreach (Artifact *inputArtifact, inputArtifacts) + safeConnect(outputArtifact, inputArtifact, m_logger); + foreach (const FileTag &fileTag, outputArtifact->fileTags) + artifactsPerFileTag[fileTag].insert(outputArtifact); + + RuleArtifactPtr ruleArtifact = RuleArtifact::create(); + ruleArtifact->fileName = outputArtifact->filePath(); + ruleArtifact->fileTags = outputArtifact->fileTags; + rule->artifacts += ruleArtifact; + } + transformer->rule = rule; + + RulesEvaluationContext::Scope s(evalContext().data()); + setupScriptEngineForFile(engine(), transformer->rule->script->fileContext, scope()); + QScriptValue prepareScriptContext = engine()->newObject(); + PrepareScriptObserver observer(engine()); + setupScriptEngineForProduct(engine(), product, transformer->rule, prepareScriptContext, + &observer); + transformer->setupInputs(engine(), prepareScriptContext); + transformer->setupOutputs(engine(), prepareScriptContext); + transformer->createCommands(rtrafo->transform, evalContext(), + ScriptEngine::argumentList(transformer->rule->script->argumentNames, + prepareScriptContext)); + if (Q_UNLIKELY(transformer->commands.isEmpty())) + throw ErrorInfo(QString("There's a transformer without commands."), rtrafo->transform->location); + } + + // Handle Transformer.explicitlyDependsOn after all transformer outputs have been created. + foreach (const TrafoPair &p, trafos) { + const ResolvedTransformerConstPtr &rtrafo = p.first; + const TransformerConstPtr &trafo = p.second; + foreach (const FileTag &tag, rtrafo->explicitlyDependsOn) { + foreach (Artifact *output, trafo->outputs) { + foreach (Artifact *dependency, artifactsPerFileTag.value(tag)) { + loggedConnect(output, dependency, m_logger); + } + } + } + } + + RulesApplicator(product, artifactsPerFileTag, m_logger).applyAllRules(); + addTargetArtifacts(product, artifactsPerFileTag, m_logger); +} + +RulesEvaluationContextPtr BuildDataResolver::evalContext() const +{ + return m_project->buildData->evaluationContext; +} + +ScriptEngine *BuildDataResolver::engine() const +{ + return evalContext()->engine(); +} + +QScriptValue BuildDataResolver::scope() const +{ + return evalContext()->scope(); +} + +} // namespace Internal +} // namespace qbs |