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; }
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); }
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; }