Loading executor.cpp +38 −16 Changes for executor.cpp: 38 added lines, 16 removed lines. Original line number Diff line number Diff line Loading @@ -33,33 +33,55 @@ const Rule* Executor::find_rule(const std::string& target, std::string& stem) { stem.clear(); return r; } // Explicit rule has no recipe - try pattern/implicit rules for recipe const Rule* pattern = mf_.find_pattern_rule(target, stem); if (pattern) { // GNU make only applies an implicit rule when its prerequisites exist // or can themselves be made. Without this check a self-similar pattern // such as "x/%: x/%_shipped" would chain forever (x/ -> x/_shipped -> // x/_shipped_shipped -> ...), growing the target name until the process // runs out of memory. A prerequisite counts as satisfiable if the file // exists, an explicit rule produces it, or it is phony. bool viable = true; for (const auto& p : pattern->prerequisites) { // Explicit rule has no recipe - try pattern/implicit rules for recipe. // GNU make considers *all* matching pattern rules and uses the one whose // prerequisites can be satisfied, preferring the most specific match // (shortest stem). Returning merely the first textual match would, for a // target like "scripts/kconfig/lexer.lex.c", pick the catch-all // "$(obj)/%: %_shipped" rule (stem "lexer.lex.c") instead of the intended // "$(obj)/%.lex.c: %.l" rule (stem "lexer"). The viability check also // breaks self-similar chains such as "x/%: x/%_shipped" (x/ -> x/_shipped // -> ...) that would otherwise grow the target name until memory runs out. auto prereqs_satisfiable = [&](const Rule* rule, const std::string& s) { for (const auto& p : rule->prerequisites) { std::string prereq = p; size_t ppct = prereq.find('%'); if (ppct != std::string::npos) { prereq = prereq.substr(0, ppct) + stem + prereq.substr(ppct + 1); prereq = prereq.substr(0, ppct) + s + prereq.substr(ppct + 1); } prereq = parser_.expand(prereq, mf_); if (prereq.empty()) continue; if (file_exists(prereq)) continue; if (mf_.find_explicit_rule(prereq)) continue; if (mf_.phony_targets.count(prereq)) continue; viable = false; break; return false; } return true; }; const Rule* best = nullptr; std::string best_stem; for (const auto& rule : mf_.rules) { if (!rule.is_pattern || rule.recipes.empty()) continue; for (const auto& t : rule.targets) { size_t pct = t.find('%'); if (pct == std::string::npos) continue; std::string prefix = t.substr(0, pct); std::string suffix = t.substr(pct + 1); if (target.size() < prefix.size() + suffix.size()) continue; if (target.substr(0, prefix.size()) != prefix) continue; if (!suffix.empty() && target.substr(target.size() - suffix.size()) != suffix) continue; std::string s = target.substr(prefix.size(), target.size() - prefix.size() - suffix.size()); if (!prereqs_satisfiable(&rule, s)) continue; if (!best || s.size() < best_stem.size()) { best = &rule; best_stem = s; } } if (viable) return pattern; } if (best) { stem = best_stem; return best; } // Built-in implicit rules static std::vector<Rule> builtins; Loading Loading
executor.cpp +38 −16 Changes for executor.cpp: 38 added lines, 16 removed lines. Original line number Diff line number Diff line Loading @@ -33,33 +33,55 @@ const Rule* Executor::find_rule(const std::string& target, std::string& stem) { stem.clear(); return r; } // Explicit rule has no recipe - try pattern/implicit rules for recipe const Rule* pattern = mf_.find_pattern_rule(target, stem); if (pattern) { // GNU make only applies an implicit rule when its prerequisites exist // or can themselves be made. Without this check a self-similar pattern // such as "x/%: x/%_shipped" would chain forever (x/ -> x/_shipped -> // x/_shipped_shipped -> ...), growing the target name until the process // runs out of memory. A prerequisite counts as satisfiable if the file // exists, an explicit rule produces it, or it is phony. bool viable = true; for (const auto& p : pattern->prerequisites) { // Explicit rule has no recipe - try pattern/implicit rules for recipe. // GNU make considers *all* matching pattern rules and uses the one whose // prerequisites can be satisfied, preferring the most specific match // (shortest stem). Returning merely the first textual match would, for a // target like "scripts/kconfig/lexer.lex.c", pick the catch-all // "$(obj)/%: %_shipped" rule (stem "lexer.lex.c") instead of the intended // "$(obj)/%.lex.c: %.l" rule (stem "lexer"). The viability check also // breaks self-similar chains such as "x/%: x/%_shipped" (x/ -> x/_shipped // -> ...) that would otherwise grow the target name until memory runs out. auto prereqs_satisfiable = [&](const Rule* rule, const std::string& s) { for (const auto& p : rule->prerequisites) { std::string prereq = p; size_t ppct = prereq.find('%'); if (ppct != std::string::npos) { prereq = prereq.substr(0, ppct) + stem + prereq.substr(ppct + 1); prereq = prereq.substr(0, ppct) + s + prereq.substr(ppct + 1); } prereq = parser_.expand(prereq, mf_); if (prereq.empty()) continue; if (file_exists(prereq)) continue; if (mf_.find_explicit_rule(prereq)) continue; if (mf_.phony_targets.count(prereq)) continue; viable = false; break; return false; } return true; }; const Rule* best = nullptr; std::string best_stem; for (const auto& rule : mf_.rules) { if (!rule.is_pattern || rule.recipes.empty()) continue; for (const auto& t : rule.targets) { size_t pct = t.find('%'); if (pct == std::string::npos) continue; std::string prefix = t.substr(0, pct); std::string suffix = t.substr(pct + 1); if (target.size() < prefix.size() + suffix.size()) continue; if (target.substr(0, prefix.size()) != prefix) continue; if (!suffix.empty() && target.substr(target.size() - suffix.size()) != suffix) continue; std::string s = target.substr(prefix.size(), target.size() - prefix.size() - suffix.size()); if (!prereqs_satisfiable(&rule, s)) continue; if (!best || s.size() < best_stem.size()) { best = &rule; best_stem = s; } } if (viable) return pattern; } if (best) { stem = best_stem; return best; } // Built-in implicit rules static std::vector<Rule> builtins; Loading