diff options
author | Christian Kandeler <christian.kandeler@qt.io> | 2018-01-29 14:04:56 +0100 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@qt.io> | 2018-02-16 09:33:47 +0000 |
commit | 24471740f276fc7fb3b662bd18ae637127368a9f (patch) | |
tree | 18a5b7657db9cd6b4edbf62da7230434b946fa61 | |
parent | fc321f1cca3b9bbcbbf0200a16aff85a85a9a148 (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>
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> ¶meters, 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()); |