Commit 467e2b40 authored by jan.koester's avatar jan.koester
Browse files

more gnu compatible lex

parent 24825f58
Loading
Loading
Loading
Loading
+38 −16
Changes for executor.cpp: 38 added lines, 16 removed lines.
Original line number Diff line number Diff line
@@ -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;