void RuleNode::apply(const Logger &logger, const std::unordered_map<QString, const ResolvedProduct *> &productsByName, const std::unordered_map<QString, const ResolvedProject *> &projectsByName, ApplicationResult *result) { ArtifactSet allCompatibleInputs = currentInputArtifacts(); const ArtifactSet explicitlyDependsOn = RulesApplicator::collectExplicitlyDependsOn(m_rule.get(), product.get()); const ArtifactSet auxiliaryInputs = RulesApplicator::collectAuxiliaryInputs(m_rule.get(), product.get()); const ArtifactSet addedInputs = allCompatibleInputs - m_oldInputArtifacts; const ArtifactSet removedInputs = m_oldInputArtifacts - allCompatibleInputs; const ArtifactSet changedInputs = changedInputArtifacts(allCompatibleInputs, explicitlyDependsOn, auxiliaryInputs); bool upToDate = changedInputs.empty() && addedInputs.empty() && removedInputs.empty(); qCDebug(lcBuildGraph).noquote().nospace() << "consider " << (m_rule->isDynamic() ? "dynamic " : "") << (m_rule->multiplex ? "multiplex " : "") << "rule node " << m_rule->toString() << "\n\tchanged: " << changedInputs.toString() << "\n\tcompatible: " << allCompatibleInputs.toString() << "\n\tadded: " << addedInputs.toString() << "\n\tremoved: " << removedInputs.toString(); ArtifactSet inputs = changedInputs; if (m_rule->multiplex) inputs = allCompatibleInputs; 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)) { upToDate = false; inputs += input; } break; } if (m_rule->multiplex) break; } // Handle rules without inputs: We want to run such a rule if and only if it has not run yet // or its transformer is not up to date regarding the prepare script. if (upToDate && (!m_rule->declaresInputs() || !m_rule->requiresInputs) && inputs.empty()) { bool hasOutputs = false; for (const Artifact * const output : filterByType<Artifact>(parents)) { if (output->transformer->rule != m_rule) continue; hasOutputs = true; if (prepareScriptNeedsRerun(output->transformer.get(), output->transformer->product().get(), productsByName, projectsByName)) { upToDate = false; break; } if (m_rule->multiplex) break; } if (!hasOutputs) upToDate = false; } if (upToDate) { qCDebug(lcExec) << "rule is up to date. Skipping."; return; } const bool mustApplyRule = !inputs.empty() || !m_rule->declaresInputs() || !m_rule->requiresInputs; // For a non-multiplex rule, the removal of an input always implies that the // corresponding outputs disappear. // For a multiplex rule, the outputs disappear only if *all* inputs are gone *and* // the rule requires inputs. This is exactly the opposite condition of whether to // re-apply the rule. const bool removedInputForcesOutputRemoval = !m_rule->multiplex || !mustApplyRule; ArtifactSet outputArtifactsToRemove; std::vector<std::pair<Artifact *, Artifact *>> connectionsToBreak; for (Artifact * const artifact : removedInputs) { if (!artifact) // dummy artifact continue; for (Artifact *parent : filterByType<Artifact>(artifact->parents)) { if (parent->transformer->rule != m_rule) { // parent was not created by our rule. continue; } // parent must always have a transformer, because it's generated. QBS_CHECK(parent->transformer); // artifact is a former input of m_rule and parent was created by m_rule // the inputs of the transformer must contain artifact QBS_CHECK(parent->transformer->inputs.contains(artifact)); if (removedInputForcesOutputRemoval) outputArtifactsToRemove += parent; else connectionsToBreak.push_back(std::make_pair(parent, artifact)); } disconnect(this, artifact); } for (const auto &connection : connectionsToBreak) disconnect(connection.first, connection.second); if (!outputArtifactsToRemove.empty()) { RulesApplicator::handleRemovedRuleOutputs(inputs, outputArtifactsToRemove, result->removedArtifacts, logger); } if (mustApplyRule) { RulesApplicator applicator(product.lock(), productsByName, projectsByName, logger); applicator.applyRule(this, inputs, explicitlyDependsOn); result->createdArtifacts = applicator.createdArtifacts(); result->invalidatedArtifacts = applicator.invalidatedArtifacts(); m_lastApplicationTime = FileTime::currentTime(); if (applicator.ruleUsesIo()) m_needsToConsiderChangedInputs = true; } else { qCDebug(lcExec).noquote() << "prepare script does not need to run"; } m_oldInputArtifacts = allCompatibleInputs; m_oldExplicitlyDependsOn = explicitlyDependsOn; m_oldAuxiliaryInputs = auxiliaryInputs; product->topLevelProject()->buildData->setDirty(); }