aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2018-01-29 14:04:56 +0100
committerChristian Kandeler <christian.kandeler@qt.io>2018-02-16 09:33:47 +0000
commit24471740f276fc7fb3b662bd18ae637127368a9f (patch)
tree18a5b7657db9cd6b4edbf62da7230434b946fa61
parentfc321f1cca3b9bbcbbf0200a16aff85a85a9a148 (diff)
Move transformer change tracking out of the build graph loader
The checks that determine whether to re-run prepare scripts and/or commands due to property or environment changes do not belong into the build graph loader. Instead, we now do them on demand during the build process. Advantages: - The code is at its "natural" place, making it easier to understand and less fragile. - There are a lot fewer unnecessary build data invalidations, speeding up both re-resolving and rebuilding in case only some rules/commands have changed. - Re-running commands due to property or environment changes no longer implies re-running prepare scripts. - We now catch property changes on generated artifacts, which was not possible before, because the build graph loader only had access to the source artifacts of the re-resolved project. Change-Id: I36b022cf631fa9e8293feec4d6f416c2686539c1 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
-rw-r--r--src/lib/corelib/buildgraph/abstractcommandexecutor.cpp2
-rw-r--r--src/lib/corelib/buildgraph/abstractcommandexecutor.h6
-rw-r--r--src/lib/corelib/buildgraph/buildgraphloader.cpp59
-rw-r--r--src/lib/corelib/buildgraph/buildgraphloader.h8
-rw-r--r--src/lib/corelib/buildgraph/executor.cpp18
-rw-r--r--src/lib/corelib/buildgraph/executor.h5
-rw-r--r--src/lib/corelib/buildgraph/executorjob.cpp1
-rw-r--r--src/lib/corelib/buildgraph/processcommandexecutor.cpp9
-rw-r--r--src/lib/corelib/buildgraph/processcommandexecutor.h2
-rw-r--r--src/lib/corelib/buildgraph/rescuableartifactdata.cpp4
-rw-r--r--src/lib/corelib/buildgraph/rescuableartifactdata.h2
-rw-r--r--src/lib/corelib/buildgraph/rulecommands.cpp8
-rw-r--r--src/lib/corelib/buildgraph/rulecommands.h5
-rw-r--r--src/lib/corelib/buildgraph/rulenode.cpp23
-rw-r--r--src/lib/corelib/buildgraph/rulenode.h7
-rw-r--r--src/lib/corelib/buildgraph/rulesapplicator.cpp25
-rw-r--r--src/lib/corelib/buildgraph/rulesapplicator.h11
-rw-r--r--src/lib/corelib/buildgraph/transformer.cpp23
-rw-r--r--src/lib/corelib/buildgraph/transformer.h7
-rw-r--r--src/lib/corelib/buildgraph/transformerchangetracking.cpp291
-rw-r--r--src/lib/corelib/buildgraph/transformerchangetracking.h19
-rw-r--r--src/lib/corelib/tools/persistence.cpp2
-rw-r--r--src/lib/corelib/tools/qttools.h8
-rw-r--r--tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs1
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp14
25 files changed, 344 insertions, 216 deletions
diff --git a/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp b/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp
index 94597e6d3..3790da9c8 100644
--- a/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp
+++ b/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp
@@ -58,7 +58,7 @@ AbstractCommandExecutor::AbstractCommandExecutor(const Logger &logger, QObject *
{
}
-void AbstractCommandExecutor::start(Transformer *transformer, const AbstractCommand *cmd)
+void AbstractCommandExecutor::start(Transformer *transformer, AbstractCommand *cmd)
{
m_transformer = transformer;
m_command = cmd;
diff --git a/src/lib/corelib/buildgraph/abstractcommandexecutor.h b/src/lib/corelib/buildgraph/abstractcommandexecutor.h
index 505fd8da0..94ac15afc 100644
--- a/src/lib/corelib/buildgraph/abstractcommandexecutor.h
+++ b/src/lib/corelib/buildgraph/abstractcommandexecutor.h
@@ -66,7 +66,7 @@ public:
virtual void cancel() = 0;
- void start(Transformer *transformer, const AbstractCommand *cmd);
+ void start(Transformer *transformer, AbstractCommand *cmd);
signals:
void reportCommandDescription(const QString &highlight, const QString &message);
@@ -74,7 +74,7 @@ signals:
protected:
virtual void doReportCommandDescription();
- const AbstractCommand *command() const { return m_command; }
+ AbstractCommand *command() const { return m_command; }
Transformer *transformer() const { return m_transformer; }
ScriptEngine *scriptEngine() const { return m_mainThreadScriptEngine; }
bool dryRun() const { return m_dryRun; }
@@ -86,7 +86,7 @@ private:
virtual void doStart() = 0;
private:
- const AbstractCommand *m_command;
+ AbstractCommand *m_command;
Transformer *m_transformer;
ScriptEngine *m_mainThreadScriptEngine;
bool m_dryRun;
diff --git a/src/lib/corelib/buildgraph/buildgraphloader.cpp b/src/lib/corelib/buildgraph/buildgraphloader.cpp
index 4b1c6588d..933d0cb34 100644
--- a/src/lib/corelib/buildgraph/buildgraphloader.cpp
+++ b/src/lib/corelib/buildgraph/buildgraphloader.cpp
@@ -47,7 +47,6 @@
#include "rulecommands.h"
#include "rulesevaluationcontext.h"
#include "transformer.h"
-#include "transformerchangetracking.h"
#include <language/artifactproperties.h>
#include <language/language.h>
@@ -333,6 +332,7 @@ void BuildGraphLoader::trackProjectChanges()
}
restoredProject->buildData->isDirty = true;
+ markTransformersForChangeTracking(allRestoredProducts);
if (!m_parameters.overrideBuildGraphData())
m_parameters.setEnvironment(restoredProject->environment);
Loader ldr(m_evalContext->engine(), m_logger);
@@ -348,16 +348,11 @@ void BuildGraphLoader::trackProjectChanges()
ldr.setStoredProfiles(restoredProject->profileConfigs);
m_result.newlyResolvedProject = ldr.loadProject(m_parameters);
- m_freshProjectsByName.insert(m_result.newlyResolvedProject->name,
- m_result.newlyResolvedProject.get());
- for (const ResolvedProjectConstPtr &p : m_result.newlyResolvedProject->allSubProjects())
- m_freshProjectsByName.insert(p->name, p.get());
QList<ResolvedProductPtr> allNewlyResolvedProducts
= m_result.newlyResolvedProject->allProducts();
for (const ResolvedProductPtr &cp : qAsConst(allNewlyResolvedProducts))
m_freshProductsByName.insert(cp->uniqueName(), cp);
- m_envChange = restoredProject->environment != m_result.newlyResolvedProject->environment;
checkAllProductsForChanges(allRestoredProducts, changedProducts);
std::shared_ptr<ProjectBuildData> oldBuildData;
@@ -625,6 +620,21 @@ bool BuildGraphLoader::hasBuildSystemFileChanged(const Set<QString> &buildSystem
return false;
}
+void BuildGraphLoader::markTransformersForChangeTracking(
+ const QList<ResolvedProductPtr> &restoredProducts)
+{
+ for (const ResolvedProductPtr &product : restoredProducts) {
+ if (!product->buildData)
+ continue;
+ for (Artifact * const artifact : filterByType<Artifact>(product->buildData->allNodes())) {
+ if (artifact->transformer) {
+ artifact->transformer->prepareScriptNeedsChangeTracking = true;
+ artifact->transformer->commandsNeedChangeTracking = true;
+ }
+ }
+ }
+}
+
void BuildGraphLoader::checkAllProductsForChanges(const QList<ResolvedProductPtr> &restoredProducts,
QList<ResolvedProductPtr> &changedProducts)
{
@@ -718,10 +728,6 @@ bool BuildGraphLoader::checkForPropertyChanges(const ResolvedProductPtr &restore
if (!restoredProduct->buildData)
return false;
- // This check must come first, as it can prevent build data rescuing.
- if (checkTransformersForChanges(restoredProduct, newlyResolvedProduct))
- return true;
-
if (restoredProduct->fileTags != newlyResolvedProduct->fileTags) {
qCDebug(lcBuildGraph) << "Product type changed from" << restoredProduct->fileTags
<< "to" << newlyResolvedProduct->fileTags;
@@ -737,27 +743,6 @@ bool BuildGraphLoader::checkForPropertyChanges(const ResolvedProductPtr &restore
return false;
}
-bool BuildGraphLoader::checkTransformersForChanges(const ResolvedProductPtr &restoredProduct,
- const ResolvedProductPtr &newlyResolvedProduct)
-{
- bool transformerChanges = false;
- Set<TransformerConstPtr> seenTransformers;
- for (Artifact *artifact : filterByType<Artifact>(restoredProduct->buildData->allNodes())) {
- const TransformerPtr transformer = artifact->transformer;
- if (!transformer || !seenTransformers.insert(transformer).second)
- continue;
- if (qbs::Internal::checkForPropertyChanges(transformer, newlyResolvedProduct,
- m_freshProductsByName, m_freshProjectsByName)
- || checkForEnvChanges(transformer, newlyResolvedProduct))
- transformerChanges = true;
- }
- if (transformerChanges) {
- qCDebug(lcBuildGraph) << "Property or environment changes in product"
- << newlyResolvedProduct->uniqueName();
- }
- return transformerChanges;
-}
-
void BuildGraphLoader::onProductRemoved(const ResolvedProductPtr &product,
ProjectBuildData *projectBuildData, bool removeArtifactsFromDisk)
{
@@ -784,15 +769,6 @@ void BuildGraphLoader::onProductRemoved(const ResolvedProductPtr &product,
}
}
-bool BuildGraphLoader::checkForEnvChanges(const TransformerPtr &restoredTrafo,
- const ResolvedProductPtr &freshProduct)
-{
- if (!m_envChange)
- return false;
- return checkForEnvironmentChanges(restoredTrafo, freshProduct,
- m_freshProductsByName, m_freshProjectsByName);
-}
-
void BuildGraphLoader::replaceFileDependencyWithArtifact(const ResolvedProductPtr &fileDepProduct,
FileDependency *filedep, Artifact *artifact)
{
@@ -894,6 +870,9 @@ void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restore
rad.depsRequestedInPrepareScript
= oldArtifact->transformer->depsRequestedInPrepareScript;
rad.depsRequestedInCommands = oldArtifact->transformer->depsRequestedInCommands;
+ rad.lastCommandExecutionTime = oldArtifact->transformer->lastCommandExecutionTime;
+ rad.lastPrepareScriptExecutionTime
+ = oldArtifact->transformer->lastPrepareScriptExecutionTime;
const ChildrenInfo &childrenInfo = childLists.value(oldArtifact);
for (Artifact * const child : qAsConst(childrenInfo.children)) {
rad.children << RescuableArtifactData::ChildData(child->product->name,
diff --git a/src/lib/corelib/buildgraph/buildgraphloader.h b/src/lib/corelib/buildgraph/buildgraphloader.h
index a37af78cb..cc29a49cf 100644
--- a/src/lib/corelib/buildgraph/buildgraphloader.h
+++ b/src/lib/corelib/buildgraph/buildgraphloader.h
@@ -96,6 +96,7 @@ private:
QList<ResolvedProductPtr> &productsWithChangedFiles);
bool hasBuildSystemFileChanged(const Set<QString> &buildSystemFiles,
const FileTime &referenceTime);
+ void markTransformersForChangeTracking(const QList<ResolvedProductPtr> &restoredProducts);
void checkAllProductsForChanges(const QList<ResolvedProductPtr> &restoredProducts,
QList<ResolvedProductPtr> &changedProducts);
bool checkProductForChanges(const ResolvedProductPtr &restoredProduct,
@@ -104,15 +105,11 @@ private:
const ResolvedProductPtr &newlyResolvedProduct);
bool checkForPropertyChanges(const ResolvedProductPtr &restoredProduct,
const ResolvedProductPtr &newlyResolvedProduct);
- bool checkTransformersForChanges(const ResolvedProductPtr &restoredProduct,
- const ResolvedProductPtr &newlyResolvedProduct);
QVariantMap propertyMapByKind(const ResolvedProductConstPtr &product, const Property &property);
void onProductRemoved(const ResolvedProductPtr &product, ProjectBuildData *projectBuildData,
bool removeArtifactsFromDisk = true);
bool checkForPropertyChanges(const TransformerPtr &restoredTrafo,
const ResolvedProductPtr &freshProduct);
- bool checkForEnvChanges(const TransformerPtr &restoredTrafo,
- const ResolvedProductPtr &freshProduct);
bool checkForPropertyChange(const Property &restoredProperty,
const QVariantMap &newProperties);
void replaceFileDependencyWithArtifact(const ResolvedProductPtr &fileDepProduct,
@@ -133,7 +130,6 @@ private:
const AllRescuableArtifactData &existingRad);
QMap<QString, ResolvedProductPtr> m_freshProductsByName;
- QMap<QString, const ResolvedProject *> m_freshProjectsByName;
RulesEvaluationContextPtr m_evalContext;
SetupProjectParameters m_parameters;
BuildGraphLoadResult m_result;
@@ -144,8 +140,6 @@ private:
// These must only be deleted at the end so we can still peek into the old look-up table.
QList<FileResourceBase *> m_objectsToDelete;
-
- bool m_envChange = false;
};
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp
index 24f077f65..be6efc1c4 100644
--- a/src/lib/corelib/buildgraph/executor.cpp
+++ b/src/lib/corelib/buildgraph/executor.cpp
@@ -51,6 +51,7 @@
#include "rulecommands.h"
#include "rulenode.h"
#include "rulesevaluationcontext.h"
+#include "transformerchangetracking.h"
#include <buildgraph/transformer.h>
#include <language/language.h>
@@ -160,11 +161,18 @@ void Executor::setProject(const TopLevelProjectPtr &project)
{
m_project = project;
m_allProducts = project->allProducts();
+ m_projectsByName.clear();
+ m_projectsByName.insert(std::make_pair(project->name, project.get()));
+ for (const ResolvedProjectPtr &p : project->allSubProjects())
+ m_projectsByName.insert(std::make_pair(p->name, p.get()));
}
void Executor::setProducts(const QList<ResolvedProductPtr> &productsToBuild)
{
m_productsToBuild = productsToBuild;
+ m_productsByName.clear();
+ for (const ResolvedProductPtr &p : productsToBuild)
+ m_productsByName.insert(std::make_pair(p->uniqueName(), p.get()));
}
class ProductPrioritySetter
@@ -424,6 +432,11 @@ bool Executor::mustExecuteTransformer(const TransformerPtr &transformer) const
return true;
}
+ if (commandsNeedRerun(transformer.get(), transformer->product().get(), m_productsByName,
+ m_projectsByName)) {
+ return true;
+ }
+
// If all artifacts in a transformer have "alwaysUpdated" set to false, that transformer
// is always run.
return !hasAlwaysUpdatedArtifacts;
@@ -493,7 +506,7 @@ void Executor::executeRuleNode(RuleNode *ruleNode)
}
RuleNode::ApplicationResult result;
- ruleNode->apply(m_logger, changedInputArtifacts, &result);
+ ruleNode->apply(m_logger, changedInputArtifacts, m_productsByName, m_projectsByName, &result);
if (result.upToDate) {
qCDebug(lcExec) << ruleNode->toString() << "is up to date. Skipping.";
@@ -823,6 +836,9 @@ void Executor::rescueOldBuildData(Artifact *artifact, bool *childrenAdded = 0)
artifact->transformer->importedFilesUsedInCommands = rad.importedFilesUsedInCommands;
artifact->transformer->depsRequestedInPrepareScript = rad.depsRequestedInPrepareScript;
artifact->transformer->depsRequestedInCommands = rad.depsRequestedInCommands;
+ artifact->transformer->lastCommandExecutionTime = rad.lastCommandExecutionTime;
+ artifact->transformer->lastPrepareScriptExecutionTime = rad.lastPrepareScriptExecutionTime;
+ artifact->transformer->commandsNeedChangeTracking = true;
artifact->setTimestamp(rad.timeStamp);
if (childrenAdded && !childrenToConnect.empty())
*childrenAdded = true;
diff --git a/src/lib/corelib/buildgraph/executor.h b/src/lib/corelib/buildgraph/executor.h
index 64ea62cb8..4127e3563 100644
--- a/src/lib/corelib/buildgraph/executor.h
+++ b/src/lib/corelib/buildgraph/executor.h
@@ -48,9 +48,12 @@
#include <logging/logger.h>
#include <tools/buildoptions.h>
#include <tools/error.h>
+#include <tools/qttools.h>
#include <QtCore/qobject.h>
+
#include <queue>
+#include <unordered_map>
QT_BEGIN_NAMESPACE
class QTimer;
@@ -164,6 +167,8 @@ private:
TopLevelProjectPtr m_project;
QList<ResolvedProductPtr> m_productsToBuild;
QList<ResolvedProductPtr> m_allProducts;
+ std::unordered_map<QString, const ResolvedProduct *> m_productsByName;
+ std::unordered_map<QString, const ResolvedProject *> m_projectsByName;
NodeSet m_roots;
Leaves m_leaves;
QList<Artifact *> m_changedSourceArtifacts;
diff --git a/src/lib/corelib/buildgraph/executorjob.cpp b/src/lib/corelib/buildgraph/executorjob.cpp
index 88dbda59f..639a17fe3 100644
--- a/src/lib/corelib/buildgraph/executorjob.cpp
+++ b/src/lib/corelib/buildgraph/executorjob.cpp
@@ -106,6 +106,7 @@ void ExecutorJob::run(Transformer *t)
t->propertiesRequestedFromArtifactInCommands.clear();
t->importedFilesUsedInCommands.clear();
t->depsRequestedInCommands.clear();
+ t->lastCommandExecutionTime = FileTime::currentTime();
QBS_CHECK(!t->outputs.empty());
m_processCommandExecutor->setProcessEnvironment(
(*t->outputs.cbegin())->product->buildEnvironment);
diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.cpp b/src/lib/corelib/buildgraph/processcommandexecutor.cpp
index ac4d266aa..2442f7a70 100644
--- a/src/lib/corelib/buildgraph/processcommandexecutor.cpp
+++ b/src/lib/corelib/buildgraph/processcommandexecutor.cpp
@@ -103,10 +103,13 @@ static QProcessEnvironment mergeEnvironments(const QProcessEnvironment &baseEnv,
void ProcessCommandExecutor::doSetup()
{
- const ProcessCommand * const cmd = processCommand();
+ ProcessCommand * const cmd = processCommand();
const QString program = ExecutableFinder(transformer()->product(),
transformer()->product()->buildEnvironment)
.findExecutable(cmd->program(), cmd->workingDir());
+ cmd->clearRelevantEnvValues();
+ for (const QString &key : cmd->relevantEnvVars())
+ cmd->addRelevantEnvValue(key, transformer()->product()->buildEnvironment.value(key));
m_commandEnvironment = mergeEnvironments(m_buildEnvironment, cmd->environment());
m_program = program;
@@ -399,9 +402,9 @@ void ProcessCommandExecutor::removeResponseFile()
m_responseFileName.clear();
}
-const ProcessCommand *ProcessCommandExecutor::processCommand() const
+ProcessCommand *ProcessCommandExecutor::processCommand() const
{
- return static_cast<const ProcessCommand *>(command());
+ return static_cast<ProcessCommand *>(command());
}
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.h b/src/lib/corelib/buildgraph/processcommandexecutor.h
index 877c0bffb..1e4b18777 100644
--- a/src/lib/corelib/buildgraph/processcommandexecutor.h
+++ b/src/lib/corelib/buildgraph/processcommandexecutor.h
@@ -80,7 +80,7 @@ private:
void sendProcessOutput();
void removeResponseFile();
- const ProcessCommand *processCommand() const;
+ ProcessCommand *processCommand() const;
private:
QString m_program;
diff --git a/src/lib/corelib/buildgraph/rescuableartifactdata.cpp b/src/lib/corelib/buildgraph/rescuableartifactdata.cpp
index 2f83283fc..37c05c5f4 100644
--- a/src/lib/corelib/buildgraph/rescuableartifactdata.cpp
+++ b/src/lib/corelib/buildgraph/rescuableartifactdata.cpp
@@ -64,6 +64,8 @@ void RescuableArtifactData::load(PersistentPool &pool)
pool.load(depsRequestedInPrepareScript);
pool.load(depsRequestedInCommands);
pool.load(commands);
+ pool.load(lastPrepareScriptExecutionTime);
+ pool.load(lastCommandExecutionTime);
pool.load(fileTags);
pool.load(properties);
}
@@ -82,6 +84,8 @@ void RescuableArtifactData::store(PersistentPool &pool) const
pool.store(depsRequestedInPrepareScript);
pool.store(depsRequestedInCommands);
pool.store(commands);
+ pool.store(lastPrepareScriptExecutionTime);
+ pool.store(lastCommandExecutionTime);
pool.store(fileTags);
pool.store(properties);
}
diff --git a/src/lib/corelib/buildgraph/rescuableartifactdata.h b/src/lib/corelib/buildgraph/rescuableartifactdata.h
index 119bcc317..87e18c2f7 100644
--- a/src/lib/corelib/buildgraph/rescuableartifactdata.h
+++ b/src/lib/corelib/buildgraph/rescuableartifactdata.h
@@ -111,6 +111,8 @@ public:
std::vector<QString> importedFilesUsedInCommands;
RequestedDependencies depsRequestedInPrepareScript;
RequestedDependencies depsRequestedInCommands;
+ FileTime lastPrepareScriptExecutionTime;
+ FileTime lastCommandExecutionTime;
// Only needed for API purposes
FileTags fileTags;
diff --git a/src/lib/corelib/buildgraph/rulecommands.cpp b/src/lib/corelib/buildgraph/rulecommands.cpp
index f235280a4..d45a246cd 100644
--- a/src/lib/corelib/buildgraph/rulecommands.cpp
+++ b/src/lib/corelib/buildgraph/rulecommands.cpp
@@ -276,6 +276,7 @@ bool ProcessCommand::equals(const AbstractCommand *otherAbstractCommand) const
&& m_stdoutFilePath == other->m_stdoutFilePath
&& m_stderrFilePath == other->m_stderrFilePath
&& m_relevantEnvVars == other->m_relevantEnvVars
+ && m_relevantEnvValues == other->m_relevantEnvValues
&& m_environment == other->m_environment;
}
@@ -336,6 +337,11 @@ QStringList ProcessCommand::relevantEnvVars() const
return vars;
}
+void ProcessCommand::addRelevantEnvValue(const QString &key, const QString &value)
+{
+ m_relevantEnvValues.insert(key, value);
+}
+
void ProcessCommand::load(PersistentPool &pool)
{
AbstractCommand::load(pool);
@@ -350,6 +356,7 @@ void ProcessCommand::load(PersistentPool &pool)
pool.load(m_responseFileThreshold);
pool.load(m_responseFileArgumentIndex);
pool.load(m_relevantEnvVars);
+ pool.load(m_relevantEnvValues);
pool.load(m_stdoutFilePath);
pool.load(m_stderrFilePath);
}
@@ -368,6 +375,7 @@ void ProcessCommand::store(PersistentPool &pool) const
pool.store(m_responseFileThreshold);
pool.store(m_responseFileArgumentIndex);
pool.store(m_relevantEnvVars);
+ pool.store(m_relevantEnvValues);
pool.store(m_stdoutFilePath);
pool.store(m_stderrFilePath);
}
diff --git a/src/lib/corelib/buildgraph/rulecommands.h b/src/lib/corelib/buildgraph/rulecommands.h
index 2fae81e99..1472611b9 100644
--- a/src/lib/corelib/buildgraph/rulecommands.h
+++ b/src/lib/corelib/buildgraph/rulecommands.h
@@ -122,6 +122,9 @@ public:
QString responseFileUsagePrefix() const { return m_responseFileUsagePrefix; }
QProcessEnvironment environment() const { return m_environment; }
QStringList relevantEnvVars() const;
+ void clearRelevantEnvValues() { m_relevantEnvValues.clear(); }
+ void addRelevantEnvValue(const QString &key, const QString &value);
+ QString relevantEnvValue(const QString &key) const { return m_relevantEnvValues.value(key); }
QString stdoutFilePath() const { return m_stdoutFilePath; }
QString stderrFilePath() const { return m_stderrFilePath; }
@@ -146,6 +149,7 @@ private:
QString m_responseFileUsagePrefix;
QProcessEnvironment m_environment;
QStringList m_relevantEnvVars;
+ QProcessEnvironment m_relevantEnvValues;
QString m_stdoutFilePath;
QString m_stderrFilePath;
};
@@ -191,6 +195,7 @@ private:
QList<AbstractCommandPtr> m_commands;
};
bool operator==(const CommandList &cl1, const CommandList &cl2);
+inline bool operator!=(const CommandList &cl1, const CommandList &cl2) { return !(cl1 == cl2); }
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/rulenode.cpp b/src/lib/corelib/buildgraph/rulenode.cpp
index ca4b06a20..ece35fcf7 100644
--- a/src/lib/corelib/buildgraph/rulenode.cpp
+++ b/src/lib/corelib/buildgraph/rulenode.cpp
@@ -45,6 +45,7 @@
#include "projectbuilddata.h"
#include "rulesapplicator.h"
#include "transformer.h"
+#include "transformerchangetracking.h"
#include <language/language.h>
#include <logging/categories.h>
@@ -78,7 +79,9 @@ QString RuleNode::toString() const
}
void RuleNode::apply(const Logger &logger, const ArtifactSet &changedInputs,
- ApplicationResult *result)
+ const std::unordered_map<QString, const ResolvedProduct *> &productsByName,
+ const std::unordered_map<QString, const ResolvedProject *> &projectsByName,
+ ApplicationResult *result)
{
ArtifactSet allCompatibleInputs = currentInputArtifacts();
const ArtifactSet addedInputs = allCompatibleInputs - m_oldInputArtifacts;
@@ -108,6 +111,22 @@ void RuleNode::apply(const Logger &logger, const ArtifactSet &changedInputs,
else
inputs += addedInputs;
+ for (Artifact * const input : allCompatibleInputs) {
+ for (const Artifact * const output : input->parentArtifacts()) {
+ if (output->transformer->rule != m_rule)
+ continue;
+ if (prepareScriptNeedsRerun(output->transformer.get(),
+ output->transformer->product().get(),
+ productsByName, projectsByName)) {
+ result->upToDate = false;
+ inputs += input;
+ }
+ break;
+ }
+ if (m_rule->multiplex)
+ break;
+ }
+
if (result->upToDate)
return;
if (!removedInputs.empty()) {
@@ -132,7 +151,7 @@ void RuleNode::apply(const Logger &logger, const ArtifactSet &changedInputs,
RulesApplicator::handleRemovedRuleOutputs(inputs, outputArtifactsToRemove, logger);
}
if (!inputs.empty() || !m_rule->declaresInputs() || !m_rule->requiresInputs) {
- RulesApplicator applicator(product.lock(), logger);
+ RulesApplicator applicator(product.lock(), productsByName, projectsByName, logger);
applicator.applyRule(m_rule, inputs);
result->createdNodes = applicator.createdArtifacts();
result->invalidatedNodes = applicator.invalidatedArtifacts();
diff --git a/src/lib/corelib/buildgraph/rulenode.h b/src/lib/corelib/buildgraph/rulenode.h
index b10447c00..50c625799 100644
--- a/src/lib/corelib/buildgraph/rulenode.h
+++ b/src/lib/corelib/buildgraph/rulenode.h
@@ -46,6 +46,8 @@
#include <language/forward_decls.h>
#include <tools/dynamictypecheck.h>
+#include <unordered_map>
+
namespace qbs {
namespace Internal {
@@ -71,7 +73,10 @@ public:
NodeSet invalidatedNodes;
};
- void apply(const Logger &logger, const ArtifactSet &changedInputs, ApplicationResult *result);
+ void apply(const Logger &logger, const ArtifactSet &changedInputs,
+ const std::unordered_map<QString, const ResolvedProduct *> &productsByName,
+ const std::unordered_map<QString, const ResolvedProject *> &projectsByName,
+ ApplicationResult *result);
void removeOldInputArtifact(Artifact *artifact) { m_oldInputArtifacts.remove(artifact); }
void load(PersistentPool &pool);
diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp
index e07194bf6..3ad01a008 100644
--- a/src/lib/corelib/buildgraph/rulesapplicator.cpp
+++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp
@@ -43,8 +43,10 @@
#include "productbuilddata.h"
#include "projectbuilddata.h"
#include "qtmocscanner.h"
+#include "rulecommands.h"
#include "rulesevaluationcontext.h"
#include "transformer.h"
+#include "transformerchangetracking.h"
#include <jsextensions/moduleproperties.h>
#include <language/artifactproperties.h>
@@ -72,8 +74,14 @@
namespace qbs {
namespace Internal {
-RulesApplicator::RulesApplicator(const ResolvedProductPtr &product, const Logger &logger)
+RulesApplicator::RulesApplicator(
+ const ResolvedProductPtr &product,
+ const std::unordered_map<QString, const ResolvedProduct *> &productsByName,
+ const std::unordered_map<QString, const ResolvedProject *> &projectsByName,
+ const Logger &logger)
: m_product(product)
+ , m_productsByName(productsByName)
+ , m_projectsByName(projectsByName)
, m_mocScanner(nullptr)
, m_logger(logger)
{
@@ -174,6 +182,7 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p
m_transformer->inputs = inputArtifacts;
m_transformer->explicitlyDependsOn = collectExplicitlyDependsOn();
m_transformer->alwaysRun = m_rule->alwaysRun;
+ m_oldTransformer.reset();
engine()->clearRequestedProperties();
@@ -257,6 +266,16 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p
if (Q_UNLIKELY(m_transformer->commands.empty()))
throw ErrorInfo(Tr::tr("There is a rule without commands: %1.")
.arg(m_rule->toString()), m_rule->prepareScript.location());
+ if (!m_oldTransformer || m_oldTransformer->outputs != m_transformer->outputs
+ || m_oldTransformer->commands != m_transformer->commands
+ || commandsNeedRerun(m_transformer.get(), m_product.get(), m_productsByName,
+ m_projectsByName)) {
+ for (Artifact * const output : outputArtifacts) {
+ output->clearTimestamp();
+ m_invalidatedArtifacts += output;
+ }
+ }
+ m_transformer->commandsNeedChangeTracking = false;
}
ArtifactSet RulesApplicator::collectOldOutputArtifacts(const ArtifactSet &inputArtifacts) const
@@ -353,10 +372,8 @@ Artifact *RulesApplicator::createOutputArtifact(const QString &filePath, const F
(*inputArtifacts.cbegin())->filePath()));
throw error;
}
- if (m_rule->declaresInputs() && m_rule->requiresInputs)
- outputArtifact->clearTimestamp();
- m_invalidatedArtifacts += outputArtifact;
m_transformer->rescueChangeTrackingData(outputArtifact->transformer);
+ m_oldTransformer = outputArtifact->transformer;
} else {
std::unique_ptr<Artifact> newArtifact(new Artifact);
newArtifact->artifactType = Artifact::Generated;
diff --git a/src/lib/corelib/buildgraph/rulesapplicator.h b/src/lib/corelib/buildgraph/rulesapplicator.h
index ee26caebd..bc3bd2bcc 100644
--- a/src/lib/corelib/buildgraph/rulesapplicator.h
+++ b/src/lib/corelib/buildgraph/rulesapplicator.h
@@ -48,9 +48,10 @@
#include <QtCore/qhash.h>
#include <QtCore/qstring.h>
-
#include <QtScript/qscriptvalue.h>
+#include <unordered_map>
+
namespace qbs {
namespace Internal {
class BuildGraphNode;
@@ -60,7 +61,10 @@ class ScriptEngine;
class RulesApplicator
{
public:
- RulesApplicator(const ResolvedProductPtr &product, const Logger &logger);
+ RulesApplicator(const ResolvedProductPtr &product,
+ const std::unordered_map<QString, const ResolvedProduct *> &productsByName,
+ const std::unordered_map<QString, const ResolvedProject *> &projectsByName,
+ const Logger &logger);
~RulesApplicator();
const NodeSet &createdArtifacts() const { return m_createdArtifacts; }
@@ -88,11 +92,14 @@ private:
QScriptValue scope() const;
const ResolvedProductPtr m_product;
+ const std::unordered_map<QString, const ResolvedProduct *> &m_productsByName;
+ const std::unordered_map<QString, const ResolvedProject *> &m_projectsByName;
NodeSet m_createdArtifacts;
NodeSet m_invalidatedArtifacts;
RuleConstPtr m_rule;
ArtifactSet m_completeInputSet;
TransformerPtr m_transformer;
+ TransformerConstPtr m_oldTransformer;
QtMocScanner *m_mocScanner;
Logger m_logger;
};
diff --git a/src/lib/corelib/buildgraph/transformer.cpp b/src/lib/corelib/buildgraph/transformer.cpp
index 92853d9da..e09e779d8 100644
--- a/src/lib/corelib/buildgraph/transformer.cpp
+++ b/src/lib/corelib/buildgraph/transformer.cpp
@@ -210,8 +210,8 @@ void Transformer::setupExplicitlyDependsOn(QScriptValue targetScriptValue)
targetScriptValue.setProperty(StringConstants::explicitlyDependsOnVar(), scriptValue);
}
-static AbstractCommandPtr createCommandFromScriptValue(const QScriptValue &scriptValue,
- const CodeLocation &codeLocation)
+AbstractCommandPtr Transformer::createCommandFromScriptValue(const QScriptValue &scriptValue,
+ const CodeLocation &codeLocation)
{
AbstractCommandPtr cmdBase;
if (scriptValue.isUndefined() || !scriptValue.isValid())
@@ -223,6 +223,12 @@ static AbstractCommandPtr createCommandFromScriptValue(const QScriptValue &scrip
cmdBase = JavaScriptCommand::create();
if (cmdBase)
cmdBase->fillFromScriptValue(&scriptValue, codeLocation);
+ if (className == StringConstants::commandType()) {
+ ProcessCommand *procCmd = static_cast<ProcessCommand *>(cmdBase.get());
+ procCmd->clearRelevantEnvValues();
+ for (const QString &key : procCmd->relevantEnvVars())
+ procCmd->addRelevantEnvValue(key, product()->buildEnvironment.value(key));
+ }
return cmdBase;
}
@@ -243,6 +249,7 @@ void Transformer::createCommands(ScriptEngine *engine, const PrivateScriptFuncti
propertiesRequestedFromArtifactInPrepareScript = engine->propertiesRequestedFromArtifact();
importedFilesUsedInPrepareScript = engine->importedFilesUsedInScript();
depsRequestedInPrepareScript = engine->requestedDependencies();
+ lastPrepareScriptExecutionTime = FileTime::currentTime();
engine->clearRequestedProperties();
if (Q_UNLIKELY(engine->hasErrorOrException(scriptValue)))
throw engine->lastError(scriptValue, script.location());
@@ -279,6 +286,10 @@ void Transformer::rescueChangeTrackingData(const TransformerConstPtr &other)
importedFilesUsedInCommands = other->importedFilesUsedInCommands;
depsRequestedInPrepareScript = other->depsRequestedInPrepareScript;
depsRequestedInCommands = other->depsRequestedInCommands;
+ lastCommandExecutionTime = other->lastCommandExecutionTime;
+ lastPrepareScriptExecutionTime = other->lastPrepareScriptExecutionTime;
+ prepareScriptNeedsChangeTracking = other->prepareScriptNeedsChangeTracking;
+ commandsNeedChangeTracking = other->commandsNeedChangeTracking;
}
void Transformer::load(PersistentPool &pool)
@@ -296,7 +307,11 @@ void Transformer::load(PersistentPool &pool)
pool.load(depsRequestedInPrepareScript);
pool.load(depsRequestedInCommands);
pool.load(commands);
+ pool.load(lastPrepareScriptExecutionTime);
+ pool.load(lastCommandExecutionTime);
pool.load(alwaysRun);
+ pool.load(prepareScriptNeedsChangeTracking);
+ pool.load(commandsNeedChangeTracking);
}
void Transformer::store(PersistentPool &pool) const
@@ -314,7 +329,11 @@ void Transformer::store(PersistentPool &pool) const
pool.store(depsRequestedInPrepareScript);
pool.store(depsRequestedInCommands);
pool.store(commands);
+ pool.store(lastPrepareScriptExecutionTime);
+ pool.store(lastCommandExecutionTime);
pool.store(alwaysRun);
+ pool.store(prepareScriptNeedsChangeTracking);
+ pool.store(commandsNeedChangeTracking);
}
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/transformer.h b/src/lib/corelib/buildgraph/transformer.h
index f1ef01ae7..180bc8d49 100644
--- a/src/lib/corelib/buildgraph/transformer.h
+++ b/src/lib/corelib/buildgraph/transformer.h
@@ -47,6 +47,7 @@
#include <language/forward_decls.h>
#include <language/property.h>
#include <language/scriptengine.h>
+#include <tools/filetime.h>
#include <QtCore/qhash.h>
@@ -76,7 +77,11 @@ public:
std::vector<QString> importedFilesUsedInCommands;
RequestedDependencies depsRequestedInPrepareScript;
RequestedDependencies depsRequestedInCommands;
+ FileTime lastPrepareScriptExecutionTime;
+ FileTime lastCommandExecutionTime;
bool alwaysRun;
+ bool prepareScriptNeedsChangeTracking = false;
+ bool commandsNeedChangeTracking = false;
static QScriptValue translateFileConfig(ScriptEngine *scriptEngine,
const Artifact *artifact,
@@ -94,6 +99,8 @@ public:
private:
Transformer();
+ AbstractCommandPtr createCommandFromScriptValue(const QScriptValue &scriptValue,
+ const CodeLocation &codeLocation);
static void setupInputs(QScriptValue targetScriptValue, const ArtifactSet &inputs,
const QString &defaultModuleName);
diff --git a/src/lib/corelib/buildgraph/transformerchangetracking.cpp b/src/lib/corelib/buildgraph/transformerchangetracking.cpp
index 07300b25b..96a612b70 100644
--- a/src/lib/corelib/buildgraph/transformerchangetracking.cpp
+++ b/src/lib/corelib/buildgraph/transformerchangetracking.cpp
@@ -27,6 +27,7 @@
****************************************************************************/
#include "transformerchangetracking.h"
+#include "projectbuilddata.h"
#include "requesteddependencies.h"
#include "rulecommands.h"
#include "transformer.h"
@@ -47,10 +48,10 @@ namespace Internal {
class TrafoChangeTracker
{
public:
- TrafoChangeTracker(const TransformerPtr &transformer,
- const ResolvedProductPtr &product,
- const QMap<QString, ResolvedProductPtr> &productsByName,
- const QMap<QString, const ResolvedProject *> projectsByName)
+ TrafoChangeTracker(const Transformer *transformer,
+ const ResolvedProduct *product,
+ const std::unordered_map<QString, const ResolvedProduct *> &productsByName,
+ const std::unordered_map<QString, const ResolvedProject *> &projectsByName)
: m_transformer(transformer),
m_product(product),
m_productsByName(productsByName),
@@ -58,59 +59,27 @@ public:
{
}
- bool propertyChangeFound();
- bool environmentChangeFound();
+ bool prepareScriptNeedsRerun() const;
+ bool commandsNeedRerun() const;
private:
- SourceArtifactConstPtr findSourceArtifact(const QString &artifactFilePath,
- QMap<QString, SourceArtifactConstPtr> &artifactMap);
QVariantMap propertyMapByKind(const Property &property) const;
- void invalidateTransformer();
bool checkForPropertyChange(const Property &restoredProperty,
const QVariantMap &newProperties) const;
bool checkForImportFileChange(const std::vector<QString> &importedFiles,
const FileTime &referenceTime,
const char *context) const;
+ const Artifact *getArtifact(const QString &filePath, const QString &productName) const;
+ const ResolvedProduct *getProduct(const QString &name) const;
- const TransformerPtr &m_transformer;
- const ResolvedProductPtr &m_product;
- const QMap<QString, ResolvedProductPtr> &m_productsByName;
- const QMap<QString, const ResolvedProject *> m_projectsByName;
+ const Transformer * const m_transformer;
+ const ResolvedProduct * const m_product;
+ const std::unordered_map<QString, const ResolvedProduct *> &m_productsByName;
+ const std::unordered_map<QString, const ResolvedProject *> &m_projectsByName;
+ mutable const ResolvedProduct * m_lastProduct = nullptr;
+ mutable const Artifact *m_lastArtifact = nullptr;
};
-bool checkForPropertyChanges(const TransformerPtr &transformer,
- const ResolvedProductPtr &product,
- const QMap<QString, ResolvedProductPtr> &productsByName,
- const QMap<QString, const ResolvedProject *> projectsByName)
-{
- return TrafoChangeTracker(transformer, product, productsByName, projectsByName)
- .propertyChangeFound();
-}
-
-bool checkForEnvironmentChanges(const TransformerPtr &transformer,
- const ResolvedProductPtr &product,
- const QMap<QString, ResolvedProductPtr> &productsByName,
- const QMap<QString, const ResolvedProject *> projectsByName)
-{
- return TrafoChangeTracker(transformer, product, productsByName, projectsByName)
- .environmentChangeFound();
-}
-
-SourceArtifactConstPtr TrafoChangeTracker::findSourceArtifact(const QString &artifactFilePath,
- QMap<QString, SourceArtifactConstPtr> &artifactMap)
-{
- SourceArtifactConstPtr &artifact = artifactMap[artifactFilePath];
- if (!artifact) {
- for (const SourceArtifactConstPtr &a : m_product->allFiles()) {
- if (a->absoluteFilePath == artifactFilePath) {
- artifact = a;
- break;
- }
- }
- }
- return artifact;
-}
-
template<typename T> static QVariantMap getParameterValue(
const QHash<T, QVariantMap> &parameters,
const QString &depName)
@@ -124,28 +93,27 @@ template<typename T> static QVariantMap getParameterValue(
QVariantMap TrafoChangeTracker::propertyMapByKind(const Property &property) const
{
- const auto getProduct = [&property, this]() {
- return property.productName == m_product->uniqueName()
- ? m_product : m_productsByName.value(property.productName);
- };
switch (property.kind) {
case Property::PropertyInModule: {
- const ResolvedProductConstPtr &p = getProduct();
+ const ResolvedProduct * const p = getProduct(property.productName);
return p ? p->moduleProperties->value() : QVariantMap();
}
case Property::PropertyInProduct: {
- const ResolvedProductConstPtr &p = getProduct();
+ const ResolvedProduct * const p = getProduct(property.productName);
return p ? p->productProperties : QVariantMap();
}
case Property::PropertyInProject: {
- const ResolvedProject *project = property.productName == m_product->project->name
- ? m_product->project.get() : m_projectsByName.value(property.productName);
- return project ? project->projectProperties() : QVariantMap();
+ if (property.productName == m_product->project->name)
+ return m_product->project->projectProperties();
+ const auto it = m_projectsByName.find(property.productName);
+ if (it != m_projectsByName.cend())
+ return it->second->projectProperties();
+ return QVariantMap();
}
case Property::PropertyInParameters: {
const int sepIndex = property.moduleName.indexOf(QLatin1Char(':'));
const QString depName = property.moduleName.left(sepIndex);
- const ResolvedProductConstPtr &p = getProduct();
+ const ResolvedProduct * const p = getProduct(property.productName);
if (!p)
return QVariantMap();
QVariantMap v = getParameterValue(p->dependencyParameters, depName);
@@ -159,78 +127,6 @@ QVariantMap TrafoChangeTracker::propertyMapByKind(const Property &property) cons
return QVariantMap();
}
-void TrafoChangeTracker::invalidateTransformer()
-{
- const JavaScriptCommandPtr &pseudoCommand = JavaScriptCommand::create();
- pseudoCommand->setSourceCode(QStringLiteral("random stuff that will cause "
- "commandsEqual() to fail"));
- m_transformer->commands.addCommand(pseudoCommand);
-}
-
-bool TrafoChangeTracker::propertyChangeFound()
-{
- // This check must come first, as it can prevent build data rescuing.
- for (const Property &property : qAsConst(m_transformer->propertiesRequestedInCommands)) {
- if (checkForPropertyChange(property, propertyMapByKind(property))) {
- invalidateTransformer();
- return true;
- }
- }
-
- QMap<QString, SourceArtifactConstPtr> artifactMap;
- for (auto it = m_transformer->propertiesRequestedFromArtifactInCommands.cbegin();
- it != m_transformer->propertiesRequestedFromArtifactInCommands.cend(); ++it) {
- const SourceArtifactConstPtr artifact
- = findSourceArtifact(it.key(), artifactMap);
- if (!artifact)
- continue;
- for (const Property &property : qAsConst(it.value())) {
- if (checkForPropertyChange(property, artifact->properties->value())) {
- invalidateTransformer();
- return true;
- }
- }
- }
-
- const FileTime referenceTime = m_transformer->product()->topLevelProject()->lastResolveTime; // TODO: This will need to be a transformer-specific timestamp
- if (checkForImportFileChange(m_transformer->importedFilesUsedInCommands, referenceTime,
- "command")) {
- invalidateTransformer();
- return true;
- }
-
- if (!m_transformer->depsRequestedInCommands.isUpToDate(m_product->topLevelProject())) {
- invalidateTransformer();
- return true;
- }
-
- for (const Property &property : qAsConst(m_transformer->propertiesRequestedInPrepareScript)) {
- if (checkForPropertyChange(property, propertyMapByKind(property)))
- return true;
- }
-
- if (checkForImportFileChange(m_transformer->importedFilesUsedInPrepareScript, referenceTime,
- "prepare script")) {
- return true;
- }
-
- for (auto it = m_transformer->propertiesRequestedFromArtifactInPrepareScript.constBegin();
- it != m_transformer->propertiesRequestedFromArtifactInPrepareScript.constEnd(); ++it) {
- const SourceArtifactConstPtr &artifact = findSourceArtifact(it.key(), artifactMap); // TODO: This can use the actual artifact data later
- if (!artifact)
- continue;
- for (const Property &property : qAsConst(it.value())) {
- if (checkForPropertyChange(property, artifact->properties->value()))
- return true;
- }
- }
-
- if (!m_transformer->depsRequestedInPrepareScript.isUpToDate(m_product->topLevelProject()))
- return true;
-
- return false;
-}
-
bool TrafoChangeTracker::checkForPropertyChange(const Property &restoredProperty,
const QVariantMap &newProperties) const
{
@@ -278,29 +174,154 @@ bool TrafoChangeTracker::checkForImportFileChange(const std::vector<QString> &im
}
if (fi.lastModified() > referenceTime) {
qCDebug(lcBuildGraph) << context << "imported file" << importedFile
- << "has been updated, need to re-run";
+ << "has been updated, need to re-run"
+ << fi.lastModified() << referenceTime;
return true;
}
}
return false;
}
-bool TrafoChangeTracker::environmentChangeFound()
+const Artifact *TrafoChangeTracker::getArtifact(const QString &filePath,
+ const QString &productName) const
{
- // TODO: Also check results of getEnv() from commands here; we currently do not track them
+ if (m_lastArtifact && m_lastArtifact->filePath() == filePath
+ && m_lastArtifact->product.get()->uniqueName() == productName) {
+ return m_lastArtifact;
+ }
+ const ResolvedProduct * const product = getProduct(productName);
+ if (!product)
+ return nullptr;
+ const QList<FileResourceBase *> &candidates
+ = product->topLevelProject()->buildData->lookupFiles(filePath);
+ const Artifact *artifact = nullptr;
+ for (const FileResourceBase * const candidate : candidates) {
+ if (candidate->fileType() == FileResourceBase::FileTypeArtifact) {
+ const Artifact * const a = static_cast<const Artifact *>(candidate);
+ if (a->product.get() == product) {
+ m_lastArtifact = artifact = a;
+ break;
+ }
+ }
+ }
+ return artifact;
+}
+
+const ResolvedProduct *TrafoChangeTracker::getProduct(const QString &name) const
+{
+ if (m_lastProduct && name == m_lastProduct->uniqueName())
+ return m_lastProduct;
+ if (name == m_product->uniqueName()) {
+ m_lastProduct = m_product;
+ return m_product;
+ }
+ const auto it = m_productsByName.find(name);
+ if (it != m_productsByName.cend()) {
+ m_lastProduct = it->second;
+ return it->second;
+ }
+ return nullptr;
+}
+
+bool TrafoChangeTracker::prepareScriptNeedsRerun() const
+{
+ for (const Property &property : qAsConst(m_transformer->propertiesRequestedInPrepareScript)) {
+ if (checkForPropertyChange(property, propertyMapByKind(property)))
+ return true;
+ }
+
+ if (checkForImportFileChange(m_transformer->importedFilesUsedInPrepareScript,
+ m_transformer->lastPrepareScriptExecutionTime, "prepare script")) {
+ return true;
+ }
+
+ for (auto it = m_transformer->propertiesRequestedFromArtifactInPrepareScript.constBegin();
+ it != m_transformer->propertiesRequestedFromArtifactInPrepareScript.constEnd(); ++it) {
+ for (const Property &property : qAsConst(it.value())) {
+ const Artifact * const artifact = getArtifact(it.key(), property.productName);
+ if (!artifact)
+ return true;
+ if (checkForPropertyChange(property, artifact->properties->value()))
+ return true;
+ }
+ }
+
+ if (!m_transformer->depsRequestedInPrepareScript.isUpToDate(m_product->topLevelProject()))
+ return true;
+
+ return false;
+}
+
+bool TrafoChangeTracker::commandsNeedRerun() const
+{
+ for (const Property &property : qAsConst(m_transformer->propertiesRequestedInCommands)) {
+ if (checkForPropertyChange(property, propertyMapByKind(property)))
+ return true;
+ }
+
+ QMap<QString, SourceArtifactConstPtr> artifactMap;
+ for (auto it = m_transformer->propertiesRequestedFromArtifactInCommands.cbegin();
+ it != m_transformer->propertiesRequestedFromArtifactInCommands.cend(); ++it) {
+ for (const Property &property : qAsConst(it.value())) {
+ const Artifact * const artifact = getArtifact(it.key(), property.productName);
+ if (!artifact)
+ return true;
+ if (checkForPropertyChange(property, artifact->properties->value()))
+ return true;
+ }
+ }
+
+ if (checkForImportFileChange(m_transformer->importedFilesUsedInCommands,
+ m_transformer->lastCommandExecutionTime, "command")) {
+ return true;
+ }
+
+ if (!m_transformer->depsRequestedInCommands.isUpToDate(m_product->topLevelProject()))
+ return true;
+
+ // TODO: Also track env access in JS commands and prepare scripts
for (const AbstractCommandPtr &c : qAsConst(m_transformer->commands.commands())) {
if (c->type() != AbstractCommand::ProcessCommandType)
continue;
- for (const QString &var : std::static_pointer_cast<ProcessCommand>(c)->relevantEnvVars()) {
- if (m_transformer->product()->topLevelProject()->environment.value(var) // TODO: These will need to get stored per transformer
- != m_product->topLevelProject()->environment.value(var)) {
- invalidateTransformer();
+ const ProcessCommandPtr &processCmd = std::static_pointer_cast<ProcessCommand>(c);
+ for (const QString &var : processCmd->relevantEnvVars()) {
+ const QString &oldValue = processCmd->relevantEnvValue(var);
+ const QString &newValue = m_product->buildEnvironment.value(var);
+ if (oldValue != newValue) {
+ qCDebug(lcBuildGraph) << "need to re-run command: value of environment variable"
+ << var << "changed from" << oldValue << "to" << newValue
+ << "for command" << processCmd->program() << "in rule"
+ << m_transformer->rule->toString();
return true;
}
}
}
+
return false;
}
+bool prepareScriptNeedsRerun(
+ Transformer *transformer, const ResolvedProduct *product,
+ const std::unordered_map<QString, const ResolvedProduct *> &productsByName,
+ const std::unordered_map<QString, const ResolvedProject *> &projectsByName)
+{
+ if (!transformer->prepareScriptNeedsChangeTracking)
+ return false;
+ transformer->prepareScriptNeedsChangeTracking = false;
+ return TrafoChangeTracker(transformer, product, productsByName, projectsByName)
+ .prepareScriptNeedsRerun();
+}
+
+bool commandsNeedRerun(Transformer *transformer, const ResolvedProduct *product,
+ const std::unordered_map<QString, const ResolvedProduct *> &productsByName,
+ const std::unordered_map<QString, const ResolvedProject *> &projectsByName)
+{
+ if (!transformer->commandsNeedChangeTracking)
+ return false;
+ transformer->commandsNeedChangeTracking = false;
+ return TrafoChangeTracker(transformer, product, productsByName, projectsByName)
+ .commandsNeedRerun();
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/transformerchangetracking.h b/src/lib/corelib/buildgraph/transformerchangetracking.h
index 4d20ffc9c..56efa458b 100644
--- a/src/lib/corelib/buildgraph/transformerchangetracking.h
+++ b/src/lib/corelib/buildgraph/transformerchangetracking.h
@@ -31,20 +31,21 @@
#include "forward_decls.h"
#include <language/forward_decls.h>
-#include <QtCore/qmap.h>
+#include <unordered_map>
namespace qbs {
namespace Internal {
-bool checkForPropertyChanges(const TransformerPtr &transformer,
- const ResolvedProductPtr &product,
- const QMap<QString, ResolvedProductPtr> &productsByName,
- const QMap<QString, const ResolvedProject *> projectsByName);
+bool prepareScriptNeedsRerun(
+ Transformer *transformer,
+ const ResolvedProduct *product,
+ const std::unordered_map<QString, const ResolvedProduct *> &productsByName,
+ const std::unordered_map<QString, const ResolvedProject *> &projectsByName);
-bool checkForEnvironmentChanges(const TransformerPtr &transformer,
- const ResolvedProductPtr &product,
- const QMap<QString, ResolvedProductPtr> &productsByName,
- const QMap<QString, const ResolvedProject *> projectsByName);
+bool commandsNeedRerun(Transformer *transformer,
+ const ResolvedProduct *product,
+ const std::unordered_map<QString, const ResolvedProduct *> &productsByName,
+ const std::unordered_map<QString, const ResolvedProject *> &projectsByName);
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/tools/persistence.cpp b/src/lib/corelib/tools/persistence.cpp
index 88f6555f8..8ec6c03f1 100644
--- a/src/lib/corelib/tools/persistence.cpp
+++ b/src/lib/corelib/tools/persistence.cpp
@@ -49,7 +49,7 @@
namespace qbs {
namespace Internal {
-static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE_112";
+static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-113";
NoBuildGraphError::NoBuildGraphError(const QString &filePath)
: ErrorInfo(Tr::tr("Build graph not found for configuration '%1'. Expected location was '%2'.")
diff --git a/src/lib/corelib/tools/qttools.h b/src/lib/corelib/tools/qttools.h
index 083a1ae03..94d1ccb0d 100644
--- a/src/lib/corelib/tools/qttools.h
+++ b/src/lib/corelib/tools/qttools.h
@@ -43,6 +43,14 @@
#include <QtCore/qhash.h>
#include <QtCore/qstringlist.h>
+#include <functional>
+
+namespace std {
+template<> struct hash<QString> {
+ std::size_t operator()(const QString &s) const { return qHash(s); }
+};
+}
+
QT_BEGIN_NAMESPACE
uint qHash(const QStringList &list);
diff --git a/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs b/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs
index 3d861f17c..ea32bbb52 100644
--- a/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs
+++ b/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs
@@ -12,6 +12,7 @@ Project {
// Depends { name: 'newDependency' }
Rule {
multiplex: true
+ inputsFromDependencies: "application"
Artifact {
fileTags: ["deps"]
filePath: product.name + '.deps'
diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp
index aa14cae17..85aa07678 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -1258,16 +1258,15 @@ void TestBlackbox::wholeArchive()
QFETCH(bool, ruleInvalidationExpected);
QFETCH(bool, dllLinkingExpected);
const QbsRunParameters resolveParams("resolve",
- QStringList("-vv") << "products.dynamiclib.linkWholeArchive:" + wholeArchiveString);
+ QStringList("products.dynamiclib.linkWholeArchive:" + wholeArchiveString));
QCOMPARE(runQbs(QbsRunParameters(resolveParams)), 0);
- const QByteArray resolveStderr = m_qbsStderr;
- QCOMPARE(runQbs(QbsRunParameters(QStringList({ "-p", "dynamiclib" }))), 0);
+ QCOMPARE(runQbs(QbsRunParameters(QStringList({ "-vvp", "dynamiclib" }))), 0);
const bool wholeArchive = !wholeArchiveString.isEmpty();
const bool outdatedVisualStudio = wholeArchive && m_qbsStderr.contains("upgrade");
const QByteArray invalidationOutput
= "Value for property 'staticlib 1:cpp.linkWholeArchive' has changed.";
if (!outdatedVisualStudio)
- QCOMPARE(resolveStderr.contains(invalidationOutput), ruleInvalidationExpected);
+ QCOMPARE(m_qbsStderr.contains(invalidationOutput), ruleInvalidationExpected);
QCOMPARE(m_qbsStdout.contains("linking"), dllLinkingExpected && !outdatedVisualStudio);
QbsRunParameters buildParams(QStringList("-p"));
buildParams.expectFailure = !wholeArchive || outdatedVisualStudio;
@@ -5649,6 +5648,7 @@ void TestBlackbox::importChangeTracking()
QVERIFY2(!m_qbsStdout.contains("running probe1 "), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running probe2"), m_qbsStdout.constData());
QVERIFY2(m_qbsStdout.contains("running custom1 prepare script"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("running custom2 prepare script"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running custom1 command"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running custom2 command"), m_qbsStdout.constData());
@@ -5660,6 +5660,7 @@ void TestBlackbox::importChangeTracking()
QVERIFY2(!m_qbsStdout.contains("running probe1 "), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running probe2"), m_qbsStdout.constData());
QVERIFY2(m_qbsStdout.contains("running custom1 prepare script"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("running custom2 prepare script"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running custom1 command"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running custom2 command"), m_qbsStdout.constData());
@@ -5670,6 +5671,8 @@ void TestBlackbox::importChangeTracking()
QVERIFY2(m_qbsStdout.contains("Resolving"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running probe1 "), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running probe2"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("running custom1 prepare script"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("running custom2 prepare script"), m_qbsStdout.constData());
QVERIFY2(m_qbsStdout.contains("running custom1 command"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running custom2 command"), m_qbsStdout.constData());
@@ -5680,6 +5683,7 @@ void TestBlackbox::importChangeTracking()
QVERIFY2(m_qbsStdout.contains("Resolving"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running probe1 "), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running probe2"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("running custom1 prepare script"), m_qbsStdout.constData());
QVERIFY2(m_qbsStdout.contains("running custom2 prepare script"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running custom1 command"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running custom2 command"), m_qbsStdout.constData());
@@ -5691,6 +5695,8 @@ void TestBlackbox::importChangeTracking()
QVERIFY2(m_qbsStdout.contains("Resolving"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running probe1 "), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running probe2"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("running custom1 prepare script"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("running custom2 prepare script"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("running custom1 command"), m_qbsStdout.constData());
QVERIFY2(m_qbsStdout.contains("running custom2 command"), m_qbsStdout.constData());