Commit 5c6a6f73 authored by jan.koester's avatar jan.koester
Browse files

test

parent 141fc6ff
Loading
Loading
Loading
Loading
+52 −1
Changes for executor.cpp: 52 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -13,6 +13,49 @@ Executor::Executor(Makefile& mf, Parser& parser) : mf_(mf), parser_(parser) {}

// ======================== Rule Lookup ========================

// Determine whether `target` can ultimately be produced, allowing GNU make's
// implicit-rule chaining (e.g. foo.o <- foo.c <- foo.c generated by another
// pattern rule). To bound the search and avoid the self-similar explosion of
// rules like "x/%: x/%_shipped", a given pattern rule may be used at most once
// along any single chain (tracked in `used`) and depth is capped.
bool Executor::can_be_made(const std::string& target_in, std::set<const Rule*>& used, int depth) {
    std::string target = Parser::normalize_target(target_in);
    if (file_exists(target)) return true;
    if (mf_.phony_targets.count(target)) return true;
    if (mf_.find_explicit_rule(target)) return true;
    if (depth > 20) return false;
    for (const auto& rule : mf_.rules) {
        if (!rule.is_pattern || rule.recipes.empty()) continue;
        if (used.count(&rule)) 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());
            used.insert(&rule);
            bool ok = true;
            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) + s + prereq.substr(ppct + 1);
                prereq = parser_.expand(prereq, mf_);
                if (prereq.empty()) continue;
                if (!can_be_made(prereq, used, depth + 1)) { ok = false; break; }
            }
            used.erase(&rule);
            if (ok) return true;
        }
    }
    return false;
}

const Rule* Executor::find_rule(const std::string& target_in, std::string& stem) {
    std::string target = Parser::normalize_target(target_in);
    const Rule* r = mf_.find_explicit_rule(target);
@@ -56,6 +99,12 @@ const Rule* Executor::find_rule(const std::string& target_in, std::string& stem)
            if (file_exists(prereq)) continue;
            if (mf_.find_explicit_rule(prereq)) continue;
            if (mf_.phony_targets.count(prereq)) continue;
            // Allow implicit-rule chaining: the prerequisite may itself be
            // produced by another pattern rule (e.g. vdso-image-64.o <-
            // vdso-image-64.c, where the .c is generated by vdso-image-%.c).
            std::set<const Rule*> used;
            used.insert(rule);
            if (can_be_made(prereq, used, 0)) continue;
            return false;
        }
        return true;
@@ -265,7 +314,9 @@ int Executor::execute_rule(const std::string& target, const Rule* rule,
    for (const auto& recipe : rule->recipes) {
        std::string expanded = parser_.expand_with_automatic(
            recipe, *mfp, target, first_prereq, prereqs, newer, stem);

        if (std::getenv("MP_DBG") && target == ".")
            std::cerr << "[REC pid=" << getpid() << "] target=. recipe=[" << recipe
                      << "] expanded=[" << expanded << "]\n";
        // A recipe line may expand (e.g. via a canned 'define' sequence) into
        // several physical lines. GNU make runs each as its own command with
        // independent @/-/+ prefix handling and its own shell.
+1 −0
Changes for executor.h: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ private:

    // Shared helpers
    const Rule* find_rule(const std::string& target, std::string& stem);
    bool can_be_made(const std::string& target, std::set<const Rule*>& used, int depth);
    std::vector<std::string> expand_prereqs(const std::string& target,
                                            const Rule* rule, const std::string& stem);
    bool needs_rebuild(const std::string& target, const std::vector<std::string>& prereqs);
+13 −1
Changes for parser.cpp: 13 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -38,6 +38,10 @@ std::string Parser::normalize_target(const std::string& s) {
        while (i < r.size() && r[i] == '/') i++; // collapse "./////"
        r = r.substr(i);
    }
    // A path consisting solely of "./" (or ".///") denotes the current
    // directory; GNU make keeps it as a directory target. Map it to "." so
    // it does not collapse to an empty (and therefore unbuildable) target.
    if (r.empty() && !s.empty()) return ".";
    return r;
}

@@ -775,6 +779,7 @@ bool Parser::try_parse_rule(const std::string& line, Makefile& mf, Rule*& curren

    auto targets = split_words(targets_str);
    if (targets.empty()) return false;
    std::string first_orig_target = targets[0];
    for (auto& t : targets) t = normalize_target(t);

    // Static pattern rule:  targets : target-pattern : prereq-patterns
@@ -913,7 +918,14 @@ bool Parser::try_parse_rule(const std::string& line, Makefile& mf, Rule*& curren
        current_rule = nullptr;
    }

    if (mf.default_target.empty() && !is_pattern && !targets.empty() && targets[0][0] != '.') {
    // GNU make: the first ordinary target becomes the default goal, unless its
    // name begins with '.' and contains no '/'. A target like "./" (the obj
    // directory in scripts/Makefile.build) DOES contain a slash and is thus a
    // valid default goal, even though normalization collapses it to ".".
    bool orig_is_special_dot =
        !first_orig_target.empty() && first_orig_target[0] == '.' &&
        first_orig_target.find('/') == std::string::npos;
    if (mf.default_target.empty() && !is_pattern && !targets.empty() && !orig_is_special_dot) {
        mf.default_target = targets[0];
    }