BuildValue ExternalCommand:: getResultForOutput(Node* node, const BuildValue& value) { // If the value was a failed or skipped command, propagate the failure. if (value.isFailedCommand() || value.isSkippedCommand()) return BuildValue::makeFailedInput(); // Otherwise, we should have a successful command -- return the actual // result for the output. assert(value.isSuccessfulCommand()); // If the node is virtual, the output is always a virtual input value. if (static_cast<BuildNode*>(node)->isVirtual()) { return BuildValue::makeVirtualInput(); } // Find the index of the output node. // // FIXME: This is O(N). We don't expect N to be large in practice, but it // could be. auto it = std::find(outputs.begin(), outputs.end(), node); assert(it != outputs.end()); auto idx = it - outputs.begin(); assert(idx < value.getNumOutputs()); auto& info = value.getNthOutputInfo(idx); if (info.isMissing()) return BuildValue::makeMissingOutput(); return BuildValue::makeExistingInput(info); }
void ExternalCommand::providePriorValue(BuildSystemCommandInterface&, core::Task*, const BuildValue& value) { if (value.isSuccessfulCommand()) { hasPriorResult = true; priorResultCommandSignature = value.getCommandSignature(); } }
bool ExternalCommand::isResultValid(BuildSystem& system, const BuildValue& value) { // Treat the command as always out-of-date, if requested. if (alwaysOutOfDate) return false; // If the prior value wasn't for a successful command, recompute. if (!value.isSuccessfulCommand()) return false; // If the command's signature has changed since it was built, rebuild. if (value.getCommandSignature() != getSignature()) return false; // Check the timestamps on each of the outputs. for (unsigned i = 0, e = outputs.size(); i != e; ++i) { auto* node = outputs[i]; // Ignore virtual outputs. if (node->isVirtual()) continue; // Rebuild if the output information has changed. // // We intentionally allow missing outputs here, as long as they haven't // changed. This is under the assumption that the commands themselves are // behaving correctly when they exit successfully, and that downstream // commands would diagnose required missing inputs. // // FIXME: CONSISTENCY: One consistency issue in this model currently is that // if the output was missing, then appears, nothing will remove it; that // results in an inconsistent build. What would be nice if we supported // per-edge annotations on whether an output was optional -- in that case we // could enforce and error on the missing output if not annotated, and we // could enable behavior to remove such output files if annotated prior to // running the command. auto info = node->getFileInfo(system.getDelegate().getFileSystem()); // If this output is mutated by the build, we can't rely on equivalence, // only existence. if (node->isMutated()) { if (value.getNthOutputInfo(i).isMissing() != info.isMissing()) return false; continue; } if (value.getNthOutputInfo(i) != info) return false; } // Otherwise, the result is ok. return true; }
bool ExternalCommand::canUpdateIfNewerWithResult(const BuildValue& result) { // Unless `allowModifiedOutputs` is specified, we always need to update if // ran. if (!allowModifiedOutputs) return false; // If it was specified, then we can update if all of our outputs simply exist. for (unsigned i = 0, e = result.getNumOutputs(); i != e; ++i) { const FileInfo& outputInfo = result.getNthOutputInfo(i); // If the output is missing, we need to rebuild. if (outputInfo.isMissing()) return false; } return true; }
void ExternalCommand::provideValue(BuildSystemCommandInterface& bsci, core::Task*, uintptr_t inputID, const BuildValue& value) { // Process the input value to see if we should skip this command. // All direct inputs should be individual node values. assert(!value.hasMultipleOutputs()); assert(value.isExistingInput() || value.isMissingInput() || value.isMissingOutput() || value.isFailedInput() || value.isVirtualInput()); // Predicate for whether the input should cause the command to skip. auto shouldSkipForInput = [&] { // If the value is an existing or virtual input, we are always good. if (value.isExistingInput() || value.isVirtualInput()) return false; // We explicitly allow running the command against a missing output, under // the expectation that responsibility for reporting this situation falls to // the command. // // FIXME: Eventually, it might be nice to harden the format so that we know // when an output was actually required versus optional. if (value.isMissingOutput()) return false; // If the value is a missing input, but those are allowed, it is ok. if (allowMissingInputs && value.isMissingInput()) return false; // For anything else, this is an error and the command should be skipped. return true; }; // Check if we need to skip the command because of this input. if (shouldSkipForInput()) { shouldSkip = true; if (value.isMissingInput()) { hasMissingInput = true; // FIXME: Design the logging and status output APIs. bsci.getDelegate().error( "", {}, (Twine("missing input '") + inputs[inputID]->getName() + "' and no rule to build it")); } } else { // If there is a missing input file (from a successful command), we always // need to run the command. if (value.isMissingOutput()) canUpdateIfNewer = false; } }
void ExternalCommand::provideValue(BuildSystemCommandInterface& bsci, core::Task*, uintptr_t inputID, const BuildValue& value) { // Process the input value to see if we should skip this command. // All direct inputs should be individual node values. assert(!value.hasMultipleOutputs()); assert(value.isExistingInput() || value.isMissingInput() || value.isMissingOutput() || value.isFailedInput() || value.isVirtualInput() || value.isSkippedCommand() || value.isDirectoryTreeSignature() || value.isStaleFileRemoval()); // If the input should cause this command to skip, how should it skip? auto getSkipValueForInput = [&]() -> llvm::Optional<BuildValue> { // If the value is an signature, existing, or virtual input, we are always // good. if (value.isDirectoryTreeSignature() | value.isExistingInput() || value.isVirtualInput() || value.isStaleFileRemoval()) return llvm::None; // We explicitly allow running the command against a missing output, under // the expectation that responsibility for reporting this situation falls to // the command. // // FIXME: Eventually, it might be nice to harden the format so that we know // when an output was actually required versus optional. if (value.isMissingOutput()) return llvm::None; // If the value is a missing input, but those are allowed, it is ok. if (value.isMissingInput()) { if (allowMissingInputs) return llvm::None; else return BuildValue::makePropagatedFailureCommand(); } // Propagate failure. if (value.isFailedInput()) return BuildValue::makePropagatedFailureCommand(); // A skipped dependency doesn't cause this command to skip. if (value.isSkippedCommand()) return llvm::None; llvm_unreachable("unexpected input"); }; // Check if we need to skip the command because of this input. auto skipValueForInput = getSkipValueForInput(); if (skipValueForInput.hasValue()) { skipValue = std::move(skipValueForInput); if (value.isMissingInput()) { hasMissingInput = true; // FIXME: Design the logging and status output APIs. bsci.getDelegate().error( "", {}, (Twine("missing input '") + inputs[inputID]->getName() + "' and no rule to build it")); } } else { // If there is a missing input file (from a successful command), we always // need to run the command. if (value.isMissingOutput()) canUpdateIfNewer = false; } }