aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@digia.com>2012-11-27 15:28:15 +0100
committerChristian Kandeler <christian.kandeler@digia.com>2012-11-28 10:04:28 +0100
commitb15ec049984dbe03e16620d9cdbcfb4e3448e58c (patch)
tree59c2d7f397e10ec41a91ba2eec41a09e8b529f1f
parent4d2230bdee293cd7b407a2c5203dc69707997e66 (diff)
Rip more functionality out of the BuildGraph class.
This is part of our ongoing quest to untangle this unholy mess. Change-Id: Icfb7a0ee955bfe33ee5bf5356a7fd80aeb6dddab Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
-rw-r--r--src/lib/api/internaljobs.cpp4
-rw-r--r--src/lib/buildgraph/buildgraph.cpp733
-rw-r--r--src/lib/buildgraph/buildgraph.h99
3 files changed, 438 insertions, 398 deletions
diff --git a/src/lib/api/internaljobs.cpp b/src/lib/api/internaljobs.cpp
index d44a13cc8..6fb182f00 100644
--- a/src/lib/api/internaljobs.cpp
+++ b/src/lib/api/internaljobs.cpp
@@ -142,7 +142,7 @@ void InternalSetupProjectJob::execute()
buildGraph->setEngine(&scriptEngine);
buildGraph->setProgressObserver(observer());
const QStringList searchPaths = Settings().searchPaths();
- const BuildProject::LoadResult loadResult = BuildProject::load(m_projectFilePath,
+ const BuildProjectLoader::LoadResult loadResult = BuildProjectLoader().load(m_projectFilePath,
buildGraph.data(), m_buildRoot, m_buildConfig, searchPaths);
ResolvedProject::Ptr rProject;
@@ -182,7 +182,7 @@ void InternalSetupProjectJob::execute()
return;
TimedActivityLogger resolveLogger(QLatin1String("Resolving build project"));
- m_buildProject = buildGraph.data()->resolveProject(rProject);
+ m_buildProject = BuildProjectResolver().resolveProject(rProject, buildGraph.data(), observer());
if (loadResult.loadedProject)
m_buildProject->rescueDependencies(loadResult.loadedProject);
buildGraph.take();
diff --git a/src/lib/buildgraph/buildgraph.cpp b/src/lib/buildgraph/buildgraph.cpp
index 7b5651b08..42ec5f067 100644
--- a/src/lib/buildgraph/buildgraph.cpp
+++ b/src/lib/buildgraph/buildgraph.cpp
@@ -151,12 +151,12 @@ void BuildGraph::setEngine(ScriptEngine *engine)
JavaScriptCommand::setupForJavaScript(m_prepareScriptScope);
}
-void BuildGraph::insert(BuildProduct::Ptr product, Artifact *n) const
+void BuildGraph::insert(BuildProduct::Ptr product, Artifact *n)
{
insert(product.data(), n);
}
-void BuildGraph::insert(BuildProduct *product, Artifact *n) const
+void BuildGraph::insert(BuildProduct *product, Artifact *n)
{
Q_ASSERT(n->product == 0);
Q_ASSERT(!n->filePath().isEmpty());
@@ -242,11 +242,6 @@ void BuildGraph::setProgressObserver(ProgressObserver *observer)
m_progressObserver = observer;
}
-const ProgressObserver *BuildGraph::progressObserver() const
-{
- return m_progressObserver;
-}
-
void BuildGraph::checkCancelation() const
{
if (m_progressObserver && m_progressObserver->canceled())
@@ -459,31 +454,6 @@ void BuildGraph::remove(Artifact *artifact) const
artifact->project->markDirty();
}
-/**
- * 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 BuildGraph::removeArtifactAndExclusiveDependents(Artifact *artifact, QList<Artifact*> *removedArtifacts)
-{
- if (removedArtifacts)
- removedArtifacts->append(artifact);
- foreach (Artifact *parent, artifact->parents) {
- bool removeParent = false;
- disconnect(parent, artifact);
- if (parent->children.isEmpty()) {
- removeParent = true;
- } else if (parent->transformer) {
- m_artifactsThatMustGetNewTransformers += parent;
- parent->transformer->inputs.remove(artifact);
- removeParent = parent->transformer->inputs.isEmpty();
- }
- if (removeParent)
- removeArtifactAndExclusiveDependents(parent, removedArtifacts);
- }
- remove(artifact);
-}
-
void BuildGraph::removeGeneratedArtifactFromDisk(Artifact *artifact)
{
if (artifact->artifactType != Artifact::Generated)
@@ -498,221 +468,6 @@ void BuildGraph::removeGeneratedArtifactFromDisk(Artifact *artifact)
qbsWarning("Cannot remove '%s'.", qPrintable(artifact->filePath()));
}
-BuildProject::Ptr BuildGraph::resolveProject(ResolvedProject::Ptr rProject)
-{
- BuildProject::Ptr project = BuildProject::Ptr(new BuildProject(this));
- project->setResolvedProject(rProject);
- if (m_progressObserver)
- m_progressObserver->initialize(Tr::tr("Resolving project"), rProject->products.count());
- foreach (ResolvedProduct::Ptr rProduct, rProject->products)
- resolveProduct(project, rProduct);
- CycleDetector().visitProject(project);
- return project;
-}
-
-BuildProduct::Ptr BuildGraph::resolveProduct(const BuildProject::Ptr &project,
- const ResolvedProduct::Ptr &rProduct)
-{
- BuildProduct::Ptr product = m_productCache.value(rProduct);
- if (product)
- return product;
-
- if (m_progressObserver) {
- checkCancelation();
- m_progressObserver->incrementProgressValue();
- }
-
- product = BuildProduct::create();
- m_productCache.insert(rProduct, product);
- product->project = project;
- product->rProduct = rProduct;
- ArtifactsPerFileTagMap artifactsPerFileTag;
-
- foreach (ResolvedProduct::Ptr t2, rProduct->dependencies) {
- if (t2 == rProduct) {
- throw Error(Tr::tr("circular using"));
- }
- BuildProduct::Ptr referencedProduct = resolveProduct(project, t2);
- product->dependencies.append(referencedProduct);
- }
-
- //add qbsFile artifact
- Artifact *qbsFileArtifact = product->lookupArtifact(rProduct->qbsFile);
- if (!qbsFileArtifact) {
- qbsFileArtifact = new Artifact(project.data());
- qbsFileArtifact->artifactType = Artifact::SourceFile;
- qbsFileArtifact->setFilePath(rProduct->qbsFile);
- qbsFileArtifact->properties = rProduct->properties;
- insert(product, qbsFileArtifact);
- }
- qbsFileArtifact->fileTags.insert("qbs");
- artifactsPerFileTag["qbs"].insert(qbsFileArtifact);
-
- // read sources
- foreach (const SourceArtifact::ConstPtr &sourceArtifact, rProduct->allFiles()) {
- QString filePath = sourceArtifact->absoluteFilePath;
- if (product->lookupArtifact(filePath)) {
- // ignore duplicate artifacts
- continue;
- }
-
- Artifact *artifact = createArtifact(product, 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;
- }
- Transformer::Ptr transformer = Transformer::create();
- transformer->inputs = inputArtifacts;
- const Rule::Ptr rule = Rule::create();
- rule->inputs = rtrafo->inputs;
- rule->jsImports = rtrafo->jsImports;
- ResolvedModule::Ptr module = ResolvedModule::create();
- module->name = rtrafo->module->name;
- rule->module = module;
- rule->script = rtrafo->transform;
- foreach (const SourceArtifact::ConstPtr &sourceArtifact, rtrafo->outputs) {
- Artifact *outputArtifact = createArtifact(product, sourceArtifact);
- outputArtifact->artifactType = Artifact::Generated;
- outputArtifact->transformer = transformer;
- transformer->outputs += outputArtifact;
- product->targetArtifacts += outputArtifact;
- foreach (Artifact *inputArtifact, inputArtifacts)
- safeConnect(outputArtifact, inputArtifact);
- foreach (const QString &fileTag, outputArtifact->fileTags)
- artifactsPerFileTag[fileTag].insert(outputArtifact);
-
- RuleArtifact::Ptr ruleArtifact = RuleArtifact::create();
- ruleArtifact->fileName = outputArtifact->filePath();
- ruleArtifact->fileTags = outputArtifact->fileTags.toList();
- rule->artifacts += ruleArtifact;
- }
- transformer->rule = rule;
-
- EngineInitializer initializer(this);
- setupScriptEngineForProduct(m_engine, rProduct, transformer->rule, m_scope);
- transformer->setupInputs(m_engine, m_scope);
- transformer->setupOutputs(m_engine, m_scope);
- createTransformerCommands(rtrafo->transform, transformer.data());
- if (transformer->commands.isEmpty())
- throw Error(QString("There's a transformer without commands."), rtrafo->transform->location);
- }
-
- applyRules(product.data(), 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;
- project->addBuildProduct(product);
- return product;
-}
-
-void BuildGraph::onProductChanged(BuildProduct::Ptr product, ResolvedProduct::Ptr changedProduct, bool *discardStoredProject)
-{
- qbsDebug() << "[BG] product '" << product->rProduct->name << "' changed.";
-
- *discardStoredProject = false;
- ArtifactsPerFileTagMap artifactsPerFileTag;
- QSet<SourceArtifact::ConstPtr> addedSourceArtifacts;
- QList<Artifact *> addedArtifacts, artifactsToRemove;
- QHash<QString, SourceArtifact::ConstPtr> oldArtifacts, newArtifacts;
-
- const QList<SourceArtifact::Ptr> oldProductAllFiles = product->rProduct->allFiles();
- foreach (const SourceArtifact::ConstPtr &a, oldProductAllFiles)
- oldArtifacts.insert(a->absoluteFilePath, a);
- foreach (const SourceArtifact::Ptr &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 += createArtifact(product, a);
- addedSourceArtifacts += a;
- }
- }
-
- foreach (const SourceArtifact::Ptr &a, oldProductAllFiles) {
- const SourceArtifact::ConstPtr 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));
- *discardStoredProject = 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;
- applyRules(product.data(), artifactsPerFileTag);
-
- // parents of removed artifacts must update their transformers
- foreach (Artifact *removedArtifact, artifactsToRemove)
- foreach (Artifact *parent, removedArtifact->parents)
- m_artifactsThatMustGetNewTransformers += parent;
- updateNodesThatMustGetNewTransformer();
-
- // delete all removed artifacts physically from the disk
- foreach (Artifact *artifact, artifactsToRemove) {
- removeGeneratedArtifactFromDisk(artifact);
- delete artifact;
- }
-}
-
void BuildGraph::updateNodesThatMustGetNewTransformer()
{
EngineInitializer engineInitializer(this);
@@ -742,7 +497,8 @@ void BuildGraph::updateNodeThatMustGetNewTransformer(Artifact *artifact)
rulesApplier.applyRule(rule);
}
-Artifact *BuildGraph::createArtifact(BuildProduct::Ptr product, SourceArtifact::ConstPtr sourceArtifact)
+Artifact *BuildGraph::createArtifact(const BuildProduct::Ptr &product,
+ const SourceArtifact::ConstPtr &sourceArtifact)
{
Artifact *artifact = new Artifact(product->project);
artifact->artifactType = Artifact::SourceFile;
@@ -886,107 +642,6 @@ static bool isConfigCompatible(const QVariantMap &userCfg, const QVariantMap &pr
return true;
}
-BuildProject::LoadResult BuildProject::load(const QString &projectFilePath, BuildGraph *bg,
- const QString &buildRoot, const QVariantMap &cfg, const QStringList &loaderSearchPaths)
-{
- LoadResult result;
- result.discardLoadedProject = false;
- const QString projectId = ResolvedProject::deriveId(cfg);
- const QString buildDir = ResolvedProject::deriveBuildDirectory(buildRoot, projectId);
- const QString buildGraphFilePath = deriveBuildGraphFilePath(buildDir, projectId);
-
- PersistentPool pool;
- qbsDebug() << "[BG] trying to load: " << buildGraphFilePath;
- if (!pool.load(buildGraphFilePath))
- return result;
- if (!isConfigCompatible(cfg, pool.headData().projectConfig)) {
- qbsDebug() << "[BG] Cannot use stored build graph: Incompatible project configuration.";
- return result;
- }
-
- const Ptr project = BuildProject::Ptr(new BuildProject(bg));
- TimedActivityLogger loadLogger(QLatin1String("Loading build graph"), QLatin1String("[BG] "));
- project->load(pool);
- foreach (const BuildProduct::Ptr &bp, project->buildProducts())
- bp->project = project;
- project->resolvedProject()->qbsFile = projectFilePath;
- project->resolvedProject()->setBuildConfiguration(pool.headData().projectConfig);
- project->resolvedProject()->buildDirectory = buildDir;
- result.loadedProject = project;
- loadLogger.finishActivity();
-
- const FileInfo bgfi(buildGraphFilePath);
- const bool projectFileChanged = bgfi.lastModified() < FileInfo(projectFilePath).lastModified();
-
- bool referencedProductRemoved = false;
- QList<BuildProduct::Ptr> changedProducts;
- foreach (BuildProduct::Ptr product, project->buildProducts()) {
- const ResolvedProduct::ConstPtr &resolvedProduct = product->rProduct;
- const FileInfo pfi(resolvedProduct->qbsFile);
- 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 SourceArtifact::ConstPtr &sourceArtifact, group->wildcards->files)
- wcFiles += sourceArtifact->absoluteFilePath;
- if (files == wcFiles)
- continue;
- changedProducts += product;
- break;
- }
- }
- }
-
- if (projectFileChanged || referencedProductRemoved || !changedProducts.isEmpty()) {
- Loader ldr(bg->engine());
- ldr.setSearchPaths(loaderSearchPaths);
- const ResolvedProject::Ptr changedProject
- = ldr.loadProject(project->resolvedProject()->qbsFile, buildRoot, cfg);
- result.changedResolvedProject = changedProject;
-
- QMap<QString, ResolvedProduct::Ptr> changedProductsMap;
- foreach (BuildProduct::Ptr product, changedProducts) {
- if (changedProductsMap.isEmpty())
- foreach (ResolvedProduct::Ptr cp, changedProject->products)
- changedProductsMap.insert(cp->name, cp);
- ResolvedProduct::Ptr changedProduct = changedProductsMap.value(product->rProduct->name);
- if (!changedProduct)
- continue;
- bg->onProductChanged(product, changedProduct, &result.discardLoadedProject);
- if (result.discardLoadedProject)
- return result;
- }
-
- QSet<QString> oldProductNames, newProductNames;
- foreach (const BuildProduct::Ptr &product, project->buildProducts())
- oldProductNames += product->rProduct->name;
- foreach (const ResolvedProduct::ConstPtr &product, changedProject->products)
- newProductNames += product->name;
- QSet<QString> addedProductNames = newProductNames - oldProductNames;
- if (!addedProductNames.isEmpty()) {
- result.discardLoadedProject = true;
- return result;
- }
- QSet<QString> removedProductsNames = oldProductNames - newProductNames;
- if (!removedProductsNames.isEmpty()) {
- foreach (const BuildProduct::Ptr &product, project->buildProducts())
- if (removedProductsNames.contains(product->rProduct->name))
- project->onProductRemoved(product);
- }
-
- CycleDetector().visitProject(project);
- }
-
- return result;
-}
-
void BuildProject::store() const
{
if (!dirty()) {
@@ -1124,20 +779,6 @@ void BuildProject::insertFileDependency(Artifact *artifact)
insertIntoArtifactLookupTable(artifact);
}
-void BuildProject::onProductRemoved(const BuildProduct::Ptr &product)
-{
- qbsDebug() << "[BG] product '" << product->rProduct->name << "' removed.";
-
- m_dirty = true;
- m_buildProducts.remove(product);
-
- // delete all removed artifacts physically from the disk
- foreach (Artifact *artifact, product->artifacts) {
- BuildGraph::disconnectAll(artifact);
- BuildGraph::removeGeneratedArtifactFromDisk(artifact);
- }
-}
-
/**
* Copies dependencies between artifacts from the other project to this project.
*/
@@ -1465,5 +1106,371 @@ QScriptValue RulesApplicator::scope() const
return engine()->currentContext()->scopeChain().first();
}
+
+BuildProject::Ptr BuildProjectResolver::resolveProject(const ResolvedProject::Ptr &resolvedProject,
+ BuildGraph *buildgraph, ProgressObserver *observer)
+{
+ m_productCache.clear();
+ m_observer = observer;
+ m_project = BuildProject::Ptr(new BuildProject(buildgraph));
+ m_project->setResolvedProject(resolvedProject);
+ if (m_observer)
+ m_observer->initialize(Tr::tr("Resolving project"), resolvedProject->products.count());
+ foreach (ResolvedProduct::Ptr rProduct, resolvedProject->products) {
+ resolveProduct(rProduct);
+ if (m_observer)
+ m_observer->incrementProgressValue();
+ }
+ CycleDetector().visitProject(m_project);
+ return m_project;
+}
+
+BuildProduct::Ptr BuildProjectResolver::resolveProduct(const ResolvedProduct::Ptr &rProduct)
+{
+ BuildProduct::Ptr product = m_productCache.value(rProduct);
+ if (product)
+ return product;
+
+ if (m_observer && m_observer->canceled())
+ throw Error(Tr::tr("Build canceled."));
+
+ product = BuildProduct::create();
+ m_productCache.insert(rProduct, product);
+ product->project = m_project;
+ product->rProduct = rProduct;
+ ArtifactsPerFileTagMap artifactsPerFileTag;
+
+ foreach (ResolvedProduct::Ptr dependency, rProduct->dependencies) {
+ if (dependency == rProduct)
+ throw Error(Tr::tr("circular using"));
+ BuildProduct::Ptr referencedProduct = resolveProduct(dependency);
+ product->dependencies.append(referencedProduct);
+ }
+
+ //add qbsFile artifact
+ Artifact *qbsFileArtifact = product->lookupArtifact(rProduct->qbsFile);
+ if (!qbsFileArtifact) {
+ qbsFileArtifact = new Artifact(m_project.data());
+ qbsFileArtifact->artifactType = Artifact::SourceFile;
+ qbsFileArtifact->setFilePath(rProduct->qbsFile);
+ qbsFileArtifact->properties = rProduct->properties;
+ buildGraph()->insert(product, qbsFileArtifact);
+ }
+ qbsFileArtifact->fileTags.insert("qbs");
+ artifactsPerFileTag["qbs"].insert(qbsFileArtifact);
+
+ // read sources
+ foreach (const SourceArtifact::ConstPtr &sourceArtifact, rProduct->allFiles()) {
+ QString filePath = sourceArtifact->absoluteFilePath;
+ if (product->lookupArtifact(filePath))
+ continue; // ignore duplicate artifacts
+
+ Artifact *artifact = buildGraph()->createArtifact(product, 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;
+ }
+ Transformer::Ptr transformer = Transformer::create();
+ transformer->inputs = inputArtifacts;
+ const Rule::Ptr rule = Rule::create();
+ rule->inputs = rtrafo->inputs;
+ rule->jsImports = rtrafo->jsImports;
+ ResolvedModule::Ptr module = ResolvedModule::create();
+ module->name = rtrafo->module->name;
+ rule->module = module;
+ rule->script = rtrafo->transform;
+ foreach (const SourceArtifact::ConstPtr &sourceArtifact, rtrafo->outputs) {
+ Artifact *outputArtifact = buildGraph()->createArtifact(product, 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);
+
+ RuleArtifact::Ptr ruleArtifact = RuleArtifact::create();
+ ruleArtifact->fileName = outputArtifact->filePath();
+ ruleArtifact->fileTags = outputArtifact->fileTags.toList();
+ rule->artifacts += ruleArtifact;
+ }
+ transformer->rule = rule;
+
+ BuildGraph::EngineInitializer initializer(buildGraph());
+ buildGraph()->setupScriptEngineForProduct(engine(), rProduct, transformer->rule, scope());
+ transformer->setupInputs(engine(), scope());
+ transformer->setupOutputs(engine(), scope());
+ buildGraph()->createTransformerCommands(rtrafo->transform, transformer.data());
+ if (transformer->commands.isEmpty())
+ throw Error(QString("There's a transformer without commands."), rtrafo->transform->location);
+ }
+
+ buildGraph()->applyRules(product.data(), 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;
+}
+
+QScriptValue BuildProjectResolver::scope() const
+{
+ return engine()->currentContext()->scopeChain().first();
+}
+
+BuildProjectLoader::LoadResult BuildProjectLoader::load(const QString &projectFilePath,
+ BuildGraph *bg, const QString &buildRoot, const QVariantMap &cfg,
+ const QStringList &loaderSearchPaths)
+{
+ m_result = LoadResult();
+
+ 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 BuildProject::Ptr project = BuildProject::Ptr(new BuildProject(bg));
+ TimedActivityLogger loadLogger(QLatin1String("Loading build graph"), QLatin1String("[BG] "));
+ project->load(pool);
+ foreach (const BuildProduct::Ptr &bp, project->buildProducts())
+ bp->project = project;
+ project->resolvedProject()->qbsFile = projectFilePath;
+ 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<BuildProduct::Ptr> changedProducts;
+ foreach (BuildProduct::Ptr product, project->buildProducts()) {
+ const ResolvedProduct::ConstPtr &resolvedProduct = product->rProduct;
+ const FileInfo pfi(resolvedProduct->qbsFile);
+ 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 SourceArtifact::ConstPtr &sourceArtifact, group->wildcards->files)
+ wcFiles += sourceArtifact->absoluteFilePath;
+ if (files == wcFiles)
+ continue;
+ changedProducts += product;
+ break;
+ }
+ }
+ }
+
+ if (projectFileChanged || referencedProductRemoved || !changedProducts.isEmpty()) {
+ Loader ldr(bg->engine());
+ ldr.setSearchPaths(loaderSearchPaths);
+ const ResolvedProject::Ptr changedProject
+ = ldr.loadProject(project->resolvedProject()->qbsFile, buildRoot, cfg);
+ m_result.changedResolvedProject = changedProject;
+
+ QMap<QString, ResolvedProduct::Ptr> changedProductsMap;
+ foreach (BuildProduct::Ptr product, changedProducts) {
+ if (changedProductsMap.isEmpty())
+ foreach (ResolvedProduct::Ptr cp, changedProject->products)
+ changedProductsMap.insert(cp->name, cp);
+ ResolvedProduct::Ptr 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 BuildProduct::Ptr &product, project->buildProducts())
+ oldProductNames += product->rProduct->name;
+ foreach (const ResolvedProduct::ConstPtr &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 BuildProduct::Ptr &product, project->buildProducts()) {
+ if (removedProductsNames.contains(product->rProduct->name))
+ onProductRemoved(product);
+ }
+ }
+
+ CycleDetector().visitProject(project);
+ }
+
+ return m_result;
+}
+
+void BuildProjectLoader::onProductRemoved(const BuildProduct::Ptr &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) {
+ BuildGraph::disconnectAll(artifact);
+ BuildGraph::removeGeneratedArtifactFromDisk(artifact);
+ }
+}
+
+void BuildProjectLoader::onProductChanged(const BuildProduct::Ptr &product,
+ const ResolvedProduct::Ptr &changedProduct)
+{
+ qbsDebug() << "[BG] product '" << product->rProduct->name << "' changed.";
+
+ ArtifactsPerFileTagMap artifactsPerFileTag;
+ QSet<SourceArtifact::ConstPtr> addedSourceArtifacts;
+ QList<Artifact *> addedArtifacts, artifactsToRemove;
+ QHash<QString, SourceArtifact::ConstPtr> oldArtifacts, newArtifacts;
+
+ const QList<SourceArtifact::Ptr> oldProductAllFiles = product->rProduct->allFiles();
+ foreach (const SourceArtifact::ConstPtr &a, oldProductAllFiles)
+ oldArtifacts.insert(a->absoluteFilePath, a);
+ foreach (const SourceArtifact::Ptr &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 += BuildGraph::createArtifact(product, a);
+ addedSourceArtifacts += a;
+ }
+ }
+
+ foreach (const SourceArtifact::Ptr &a, oldProductAllFiles) {
+ const SourceArtifact::ConstPtr 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;
+
+ BuildGraph * const bg = product->project->buildGraph();
+
+ // apply rules for new artifacts
+ foreach (Artifact *artifact, addedArtifacts)
+ foreach (const QString &ft, artifact->fileTags)
+ artifactsPerFileTag[ft] += artifact;
+ bg->applyRules(product.data(), artifactsPerFileTag);
+
+ // parents of removed artifacts must update their transformers
+ foreach (Artifact *removedArtifact, artifactsToRemove)
+ foreach (Artifact *parent, removedArtifact->parents)
+ bg->addToArtifactsThatMustGetNewTransformers(parent);
+ bg->updateNodesThatMustGetNewTransformer();
+
+ // delete all removed artifacts physically from the disk
+ foreach (Artifact *artifact, artifactsToRemove) {
+ bg->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)
+{
+ BuildGraph * const bg = artifact->project->buildGraph();
+ 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) {
+ bg->addToArtifactsThatMustGetNewTransformers(parent);
+ parent->transformer->inputs.remove(artifact);
+ removeParent = parent->transformer->inputs.isEmpty();
+ }
+ if (removeParent)
+ removeArtifactAndExclusiveDependents(parent, removedArtifacts);
+ }
+ bg->remove(artifact);
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/buildgraph/buildgraph.h b/src/lib/buildgraph/buildgraph.h
index 5959fbc20..f3a26ddf1 100644
--- a/src/lib/buildgraph/buildgraph.h
+++ b/src/lib/buildgraph/buildgraph.h
@@ -87,10 +87,13 @@ private:
};
class BuildGraph;
+class BuildProjectLoader;
+class BuildProjectResolver;
class BuildProject : public PersistentObject
{
- friend class BuildGraph;
+ friend class BuildProjectLoader;
+ friend class BuildProjectResolver;
public:
typedef QSharedPointer<BuildProject> Ptr;
typedef QSharedPointer<const BuildProject> ConstPtr;
@@ -98,15 +101,6 @@ public:
BuildProject(BuildGraph *bg);
~BuildProject();
- struct LoadResult
- {
- ResolvedProject::Ptr changedResolvedProject;
- BuildProject::Ptr loadedProject;
- bool discardLoadedProject;
- };
-
- static LoadResult load(const QString &projectFilePath, BuildGraph *bg, const QString &buildRoot,
- const QVariantMap &cfg, const QStringList &loaderSearchPaths);
void store() const;
static QString deriveBuildGraphFilePath(const QString &buildDir, const QString projectId);
QString buildGraphFilePath() const;
@@ -123,8 +117,6 @@ public:
void insertFileDependency(Artifact *artifact);
void rescueDependencies(const BuildProject::Ptr &other);
- void onProductRemoved(const BuildProduct::Ptr &product);
-
private:
void load(PersistentPool &pool);
void store(PersistentPool &pool) const;
@@ -153,35 +145,47 @@ private:
class BuildGraph
{
public:
+ class EngineInitializer
+ {
+ public:
+ EngineInitializer(BuildGraph *bg);
+ ~EngineInitializer();
+
+ private:
+ BuildGraph *buildGraph;
+ };
+
BuildGraph();
~BuildGraph();
void setEngine(ScriptEngine *engine);
ScriptEngine *engine() { return m_engine; }
- BuildProject::Ptr resolveProject(ResolvedProject::Ptr);
-
void applyRules(BuildProduct *product, ArtifactsPerFileTagMap &artifactsPerFileTag);
void setProgressObserver(ProgressObserver *observer);
- const ProgressObserver *progressObserver() const;
void checkCancelation() const;
+ static Artifact *createArtifact(const BuildProduct::Ptr &product,
+ const SourceArtifact::ConstPtr &sourceArtifact);
+
static bool findPath(Artifact *u, Artifact *v, QList<Artifact*> &path);
static void connect(Artifact *p, Artifact *c);
static void loggedConnect(Artifact *u, Artifact *v);
static bool safeConnect(Artifact *u, Artifact *v);
- void insert(BuildProduct::Ptr target, Artifact *n) const;
- void insert(BuildProduct *target, Artifact *n) const;
+ static void insert(BuildProduct::Ptr target, Artifact *n);
+ static void insert(BuildProduct *target, Artifact *n);
void remove(Artifact *artifact) const;
- void removeArtifactAndExclusiveDependents(Artifact *artifact, QList<Artifact*> *removedArtifacts = 0);
static void removeGeneratedArtifactFromDisk(Artifact *artifact);
- void onProductChanged(BuildProduct::Ptr product, ResolvedProduct::Ptr changedProduct, bool *discardStoredProject);
void updateNodesThatMustGetNewTransformer();
void removeFromArtifactsThatMustGetNewTransformers(Artifact *a) {
m_artifactsThatMustGetNewTransformers -= a;
}
+ void addToArtifactsThatMustGetNewTransformers(Artifact *a) {
+ m_artifactsThatMustGetNewTransformers += a;
+ }
+
void createTransformerCommands(const PrepareScript::ConstPtr &script, Transformer *transformer);
static void setupScriptEngineForProduct(ScriptEngine *scriptEngine,
@@ -195,27 +199,13 @@ public:
private:
void initEngine();
void cleanupEngine();
- BuildProduct::Ptr resolveProduct(const BuildProject::Ptr &project,
- const ResolvedProduct::Ptr &rProduct);
- Artifact *createArtifact(BuildProduct::Ptr product, SourceArtifact::ConstPtr sourceArtifact);
void updateNodeThatMustGetNewTransformer(Artifact *artifact);
- class EngineInitializer
- {
- public:
- EngineInitializer(BuildGraph *bg);
- ~EngineInitializer();
-
- private:
- BuildGraph *buildGraph;
- };
-
ProgressObserver *m_progressObserver;
ScriptEngine *m_engine;
unsigned int m_initEngineCalls;
QScriptValue m_scope;
QScriptValue m_prepareScriptScope;
- QHash<ResolvedProduct::Ptr, BuildProduct::Ptr> m_productCache;
QHash<QString, QScriptProgram> m_scriptProgramCache;
mutable QSet<Artifact *> m_artifactsThatMustGetNewTransformers;
@@ -248,6 +238,49 @@ private:
QSharedPointer<Transformer> m_transformer;
};
+class BuildProjectResolver
+{
+public:
+ BuildProject::Ptr resolveProject(const ResolvedProject::Ptr &resolvedProject,
+ BuildGraph *buildgraph, ProgressObserver *observer = 0);
+
+private:
+ BuildProduct::Ptr resolveProduct(const ResolvedProduct::Ptr &rProduct);
+
+ BuildGraph *buildGraph() const { return m_project->buildGraph(); }
+ ScriptEngine *engine() const { return buildGraph()->engine(); }
+ QScriptValue scope() const;
+
+ BuildProject::Ptr m_project;
+ ProgressObserver *m_observer;
+ QHash<ResolvedProduct::Ptr, BuildProduct::Ptr> m_productCache;
+};
+
+class BuildProjectLoader
+{
+public:
+ struct LoadResult
+ {
+ LoadResult() : discardLoadedProject(false) {}
+
+ ResolvedProject::Ptr changedResolvedProject;
+ BuildProject::Ptr loadedProject;
+ bool discardLoadedProject;
+ };
+
+ LoadResult load(const QString &projectFilePath, BuildGraph *bg, const QString &buildRoot,
+ const QVariantMap &cfg, const QStringList &loaderSearchPaths);
+
+private:
+ void onProductRemoved(const BuildProduct::Ptr &product);
+ void onProductChanged(const BuildProduct::Ptr &product,
+ const ResolvedProduct::Ptr &changedProduct);
+ void removeArtifactAndExclusiveDependents(Artifact *artifact,
+ QList<Artifact*> *removedArtifacts = 0);
+
+ LoadResult m_result;
+};
+
// debugging helper
QString fileName(Artifact *n);