Loading executor.cpp +16 −4 Changes for executor.cpp: 16 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -246,11 +246,14 @@ const Rule* Executor::find_rule(const std::string& target_in, std::string& stem) } std::vector<std::string> Executor::expand_prereqs(const std::string& target, const Rule* rule, const std::string& stem) { const Rule* rule, const std::string& stem, bool as_written) { std::vector<std::string> prereqs; auto add_prereqs = [&](const Rule* r, bool pattern) { for (const auto& p : r->prerequisites) { const auto& src = (as_written && !r->prerequisites_raw.empty()) ? r->prerequisites_raw : r->prerequisites; for (const auto& p : src) { std::string ep = p; if (pattern) { size_t pct = ep.find('%'); Loading @@ -262,6 +265,7 @@ std::vector<std::string> Executor::expand_prereqs(const std::string& target, std::string expanded = parser_.expand(ep, mf_); auto words = Parser::split_words(expanded); for (auto& w : words) { std::string written = Parser::strip_dot_slash(w); w = Parser::normalize_target(w); // GNU make directory search: a prerequisite that does not // exist locally and has no local rule may still be found via Loading @@ -270,7 +274,8 @@ std::vector<std::string> Executor::expand_prereqs(const std::string& target, // located via "vpath %.c $(ZSTD_SUBDIR)"). Substitute the // resolved path so $<, $^, and mtime comparisons see the file // make actually found rather than the bare, nonexistent name. w = resolve_vpath(w); std::string found = resolve_vpath(w); w = (as_written && found == w) ? written : found; } prereqs.insert(prereqs.end(), words.begin(), words.end()); } Loading Loading @@ -581,8 +586,15 @@ std::map<std::string, std::string> Executor::compute_target_scope( } int Executor::execute_rule(const std::string& target, const Rule* rule, const std::string& stem, const std::vector<std::string>& prereqs, const std::string& stem, const std::vector<std::string>& prereqs_in, const std::map<std::string, std::string>& scope) { // $^/$</$+/$? as written in the makefile, not normalize_target()'s // lookup names ("a//b" stays "a//b", like GNU make); same files std::vector<std::string> prereqs = prereqs_in; if (!rule->double_colon) { auto written = expand_prereqs(target, rule, stem, true); if (written.size() == prereqs.size()) prereqs.swap(written); } std::string first_prereq = prereqs.empty() ? "" : prereqs[0]; const Makefile* mfp = &mf_; Loading executor.h +5 −1 Changes for executor.h: 5 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -114,8 +114,12 @@ private: // to prerequisites). Returns the effective override map for `target`. std::map<std::string, std::string> compute_target_scope( const std::string& target, const std::map<std::string, std::string>& inherited); // as_written: the prerequisites spelled as in the makefile (vpath // resolution still applied), for the automatic variables; otherwise // normalize_target()'s names, for lookups. Same order and count. std::vector<std::string> expand_prereqs(const std::string& target, const Rule* rule, const std::string& stem); const Rule* rule, const std::string& stem, bool as_written = false); // Order-only prerequisites ('target: normal-prereqs | order-only-prereqs'). // Must exist/be built before the recipe runs, but (unlike expand_prereqs) // never force a rebuild by themselves and are excluded from the automatic Loading parser.cpp +53 −7 Changes for parser.cpp: 53 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -70,6 +70,24 @@ std::string Parser::normalize_target(const std::string& s) { return r; } std::string Parser::strip_dot_slash(const std::string& s) { std::string r = s; while (r.size() >= 2 && r[0] == '.' && r[1] == '/') { size_t i = 2; while (i < r.size() && r[i] == '/') i++; r = r.substr(i); } return r.empty() && !s.empty() ? "." : r; } // rule.prerequisites_raw: set only when some prerequisite's written // spelling differs from its normalized one static void set_raw_prereqs(Rule& rule, std::vector<std::string> raw) { if (raw.size() != rule.prerequisites.size()) return; for (auto& p : raw) p = Parser::strip_dot_slash(p); if (raw != rule.prerequisites) rule.prerequisites_raw = std::move(raw); } bool Parser::glob_match(const std::string& pattern, const std::string& str) { size_t pi = 0, si = 0; size_t star_p = std::string::npos, star_s = 0; Loading Loading @@ -549,6 +567,22 @@ void Parser::process_line(const std::string& line, Makefile& mf, Rule*& current_ return; } // "override NAME op VALUE" (libbpf's "override CFLAGS += $(INCLUDES)"); // without this, "override CFLAGS" became the variable's name if (trimmed.size() > 9 && trimmed.compare(0, 9, "override ") == 0) { std::string rest = trim(trimmed.substr(9)); if (rest.compare(0, 7, "export ") == 0) { rest = trim(rest.substr(7)); size_t ne = rest.find_first_of(":+?!="); std::string nm = trim(rest.substr(0, ne)); if (!nm.empty()) mf.exported.insert(expand(nm, mf)); } if (try_parse_assignment(rest, mf, true)) { current_rule = nullptr; return; } } // export / unexport if (trimmed == "export") { mf.export_all = true; Loading Loading @@ -653,7 +687,7 @@ void Parser::process_line(const std::string& line, Makefile& mf, Rule*& current_ current_rule = nullptr; } bool Parser::try_parse_assignment(const std::string& line, Makefile& mf) { bool Parser::try_parse_assignment(const std::string& line, Makefile& mf, bool is_override) { int depth = 0; size_t eq_pos = std::string::npos; Loading Loading @@ -702,7 +736,8 @@ bool Parser::try_parse_assignment(const std::string& line, Makefile& mf) { if (!value.empty() && value[0] == ' ') value = value.substr(1); auto it = mf.variables.find(name); if (it != mf.variables.end()) { if (!it->second.from_cmdline) { bool locked = it->second.from_cmdline || it->second.is_override; if (is_override || !locked) { // GNU make: when '+=' appends to a simply-expanded (':=') // variable, the appended text is expanded immediately; // for a recursive variable it is stored verbatim. Without Loading @@ -713,9 +748,13 @@ bool Parser::try_parse_assignment(const std::string& line, Makefile& mf) { ? expand(value, mf) : value; if (it->second.value.empty()) it->second.value = appended; else it->second.value += " " + appended; if (is_override) { it->second.from_cmdline = false; it->second.is_override = true; } } } else { mf.variables[name] = {value, VarFlavor::Recursive, false}; mf.variables[name] = {value, VarFlavor::Recursive, false, is_override}; } return true; } else if (prev == '?') { Loading @@ -735,8 +774,9 @@ bool Parser::try_parse_assignment(const std::string& line, Makefile& mf) { cmd = expand(cmd, mf); std::string value = func_shell(cmd); auto it = mf.variables.find(name); if (it == mf.variables.end() || !it->second.from_cmdline) { mf.variables[name] = {value, VarFlavor::Simple, false}; if (is_override || it == mf.variables.end() || !(it->second.from_cmdline || it->second.is_override)) { mf.variables[name] = {value, VarFlavor::Simple, false, is_override}; } return true; } Loading @@ -749,13 +789,14 @@ bool Parser::try_parse_assignment(const std::string& line, Makefile& mf) { if (name.empty()) return false; auto it = mf.variables.find(name); if (it != mf.variables.end() && it->second.from_cmdline) return true; if (!is_override && it != mf.variables.end() && (it->second.from_cmdline || it->second.is_override)) return true; if (flavor == VarFlavor::Simple) { value = expand(value, mf); } mf.variables[name] = {value, flavor, false}; mf.variables[name] = {value, flavor, false, is_override}; return true; } Loading Loading @@ -904,11 +945,13 @@ bool Parser::try_parse_rule(const std::string& line, Makefile& mf, Rule*& curren } else { pprereqs = split_words(ppat); } std::vector<std::string> pprereqs_raw = pprereqs; for (auto& p : pprereqs) p = normalize_target(p); for (auto& p : poo) p = normalize_target(p); Rule rule; rule.targets = targets; rule.prerequisites = pprereqs; set_raw_prereqs(rule, pprereqs_raw); rule.order_only_prereqs = poo; rule.is_pattern = false; rule.target_pattern = tpat; Loading Loading @@ -1019,6 +1062,7 @@ bool Parser::try_parse_rule(const std::string& line, Makefile& mf, Rule*& curren }; glob_words(prereqs); glob_words(order_only); std::vector<std::string> prereqs_raw = prereqs; for (auto& p : prereqs) p = normalize_target(p); for (auto& p : order_only) p = normalize_target(p); Loading Loading @@ -1093,6 +1137,7 @@ bool Parser::try_parse_rule(const std::string& line, Makefile& mf, Rule*& curren mf.rules.push_back({targets, prereqs, order_only, {}, is_pattern, ""}); current_rule = &mf.rules.back(); set_raw_prereqs(*current_rule, prereqs_raw); current_rule->double_colon = double_colon && !is_pattern; if (has_inline_recipe) { Loading Loading @@ -1997,6 +2042,7 @@ std::string Parser::expand_function(const std::string& name, const std::string& std::string var = trim(do_expand(args, mf, auto_vars)); auto it = mf.variables.find(var); if (it == mf.variables.end()) return "undefined"; if (it->second.is_override) return "override"; if (it->second.from_cmdline) return "command line"; return "file"; } Loading parser.h +12 −1 Changes for parser.h: 12 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ struct Variable { std::string value; VarFlavor flavor = VarFlavor::Recursive; bool from_cmdline = false; // set by "override NAME op VALUE": beats the command line, and later // plain assignments in the makefile leave it alone (GNU make) bool is_override = false; }; struct Rule { Loading @@ -32,6 +35,11 @@ struct Rule { // "target :: prereqs": each double-colon rule of a target is // independent, with its own recipe run when its own prereqs require it. bool double_colon = false; // The prerequisites as written (only a leading "./" stripped), when that // differs from normalize_target()'s spelling in `prerequisites`: lookups // use the normalized names, $^/$</$+/$? the written ones like GNU make // (tools/build's "$(filter $(obj-y),$^)" with "dir//x.o" in obj-y). std::vector<std::string> prerequisites_raw = {}; }; struct Makefile { Loading Loading @@ -112,6 +120,9 @@ public: // "kernel/bounds.s". Apply this consistently to targets, prerequisites, // patterns, and requested goals. static std::string normalize_target(const std::string& s); // Only the leading "./" segments normalize_target() strips, which GNU // make strips from prerequisite names too. static std::string strip_dot_slash(const std::string& s); private: std::string error_; Loading @@ -132,7 +143,7 @@ private: // Detect 'NAME op VALUE' target-specific variable bodies after a ':'. bool parse_target_var(const std::string& rest, std::string& name, std::string& op, std::string& value) const; bool try_parse_assignment(const std::string& line, Makefile& mf); bool try_parse_assignment(const std::string& line, Makefile& mf, bool is_override = false); bool try_parse_rule(const std::string& line, Makefile& mf, Rule*& current_rule); void handle_include(const std::string& args, Makefile& mf, bool optional); bool handle_conditional(const std::string& line, Makefile& mf); Loading Loading
executor.cpp +16 −4 Changes for executor.cpp: 16 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -246,11 +246,14 @@ const Rule* Executor::find_rule(const std::string& target_in, std::string& stem) } std::vector<std::string> Executor::expand_prereqs(const std::string& target, const Rule* rule, const std::string& stem) { const Rule* rule, const std::string& stem, bool as_written) { std::vector<std::string> prereqs; auto add_prereqs = [&](const Rule* r, bool pattern) { for (const auto& p : r->prerequisites) { const auto& src = (as_written && !r->prerequisites_raw.empty()) ? r->prerequisites_raw : r->prerequisites; for (const auto& p : src) { std::string ep = p; if (pattern) { size_t pct = ep.find('%'); Loading @@ -262,6 +265,7 @@ std::vector<std::string> Executor::expand_prereqs(const std::string& target, std::string expanded = parser_.expand(ep, mf_); auto words = Parser::split_words(expanded); for (auto& w : words) { std::string written = Parser::strip_dot_slash(w); w = Parser::normalize_target(w); // GNU make directory search: a prerequisite that does not // exist locally and has no local rule may still be found via Loading @@ -270,7 +274,8 @@ std::vector<std::string> Executor::expand_prereqs(const std::string& target, // located via "vpath %.c $(ZSTD_SUBDIR)"). Substitute the // resolved path so $<, $^, and mtime comparisons see the file // make actually found rather than the bare, nonexistent name. w = resolve_vpath(w); std::string found = resolve_vpath(w); w = (as_written && found == w) ? written : found; } prereqs.insert(prereqs.end(), words.begin(), words.end()); } Loading Loading @@ -581,8 +586,15 @@ std::map<std::string, std::string> Executor::compute_target_scope( } int Executor::execute_rule(const std::string& target, const Rule* rule, const std::string& stem, const std::vector<std::string>& prereqs, const std::string& stem, const std::vector<std::string>& prereqs_in, const std::map<std::string, std::string>& scope) { // $^/$</$+/$? as written in the makefile, not normalize_target()'s // lookup names ("a//b" stays "a//b", like GNU make); same files std::vector<std::string> prereqs = prereqs_in; if (!rule->double_colon) { auto written = expand_prereqs(target, rule, stem, true); if (written.size() == prereqs.size()) prereqs.swap(written); } std::string first_prereq = prereqs.empty() ? "" : prereqs[0]; const Makefile* mfp = &mf_; Loading
executor.h +5 −1 Changes for executor.h: 5 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -114,8 +114,12 @@ private: // to prerequisites). Returns the effective override map for `target`. std::map<std::string, std::string> compute_target_scope( const std::string& target, const std::map<std::string, std::string>& inherited); // as_written: the prerequisites spelled as in the makefile (vpath // resolution still applied), for the automatic variables; otherwise // normalize_target()'s names, for lookups. Same order and count. std::vector<std::string> expand_prereqs(const std::string& target, const Rule* rule, const std::string& stem); const Rule* rule, const std::string& stem, bool as_written = false); // Order-only prerequisites ('target: normal-prereqs | order-only-prereqs'). // Must exist/be built before the recipe runs, but (unlike expand_prereqs) // never force a rebuild by themselves and are excluded from the automatic Loading
parser.cpp +53 −7 Changes for parser.cpp: 53 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -70,6 +70,24 @@ std::string Parser::normalize_target(const std::string& s) { return r; } std::string Parser::strip_dot_slash(const std::string& s) { std::string r = s; while (r.size() >= 2 && r[0] == '.' && r[1] == '/') { size_t i = 2; while (i < r.size() && r[i] == '/') i++; r = r.substr(i); } return r.empty() && !s.empty() ? "." : r; } // rule.prerequisites_raw: set only when some prerequisite's written // spelling differs from its normalized one static void set_raw_prereqs(Rule& rule, std::vector<std::string> raw) { if (raw.size() != rule.prerequisites.size()) return; for (auto& p : raw) p = Parser::strip_dot_slash(p); if (raw != rule.prerequisites) rule.prerequisites_raw = std::move(raw); } bool Parser::glob_match(const std::string& pattern, const std::string& str) { size_t pi = 0, si = 0; size_t star_p = std::string::npos, star_s = 0; Loading Loading @@ -549,6 +567,22 @@ void Parser::process_line(const std::string& line, Makefile& mf, Rule*& current_ return; } // "override NAME op VALUE" (libbpf's "override CFLAGS += $(INCLUDES)"); // without this, "override CFLAGS" became the variable's name if (trimmed.size() > 9 && trimmed.compare(0, 9, "override ") == 0) { std::string rest = trim(trimmed.substr(9)); if (rest.compare(0, 7, "export ") == 0) { rest = trim(rest.substr(7)); size_t ne = rest.find_first_of(":+?!="); std::string nm = trim(rest.substr(0, ne)); if (!nm.empty()) mf.exported.insert(expand(nm, mf)); } if (try_parse_assignment(rest, mf, true)) { current_rule = nullptr; return; } } // export / unexport if (trimmed == "export") { mf.export_all = true; Loading Loading @@ -653,7 +687,7 @@ void Parser::process_line(const std::string& line, Makefile& mf, Rule*& current_ current_rule = nullptr; } bool Parser::try_parse_assignment(const std::string& line, Makefile& mf) { bool Parser::try_parse_assignment(const std::string& line, Makefile& mf, bool is_override) { int depth = 0; size_t eq_pos = std::string::npos; Loading Loading @@ -702,7 +736,8 @@ bool Parser::try_parse_assignment(const std::string& line, Makefile& mf) { if (!value.empty() && value[0] == ' ') value = value.substr(1); auto it = mf.variables.find(name); if (it != mf.variables.end()) { if (!it->second.from_cmdline) { bool locked = it->second.from_cmdline || it->second.is_override; if (is_override || !locked) { // GNU make: when '+=' appends to a simply-expanded (':=') // variable, the appended text is expanded immediately; // for a recursive variable it is stored verbatim. Without Loading @@ -713,9 +748,13 @@ bool Parser::try_parse_assignment(const std::string& line, Makefile& mf) { ? expand(value, mf) : value; if (it->second.value.empty()) it->second.value = appended; else it->second.value += " " + appended; if (is_override) { it->second.from_cmdline = false; it->second.is_override = true; } } } else { mf.variables[name] = {value, VarFlavor::Recursive, false}; mf.variables[name] = {value, VarFlavor::Recursive, false, is_override}; } return true; } else if (prev == '?') { Loading @@ -735,8 +774,9 @@ bool Parser::try_parse_assignment(const std::string& line, Makefile& mf) { cmd = expand(cmd, mf); std::string value = func_shell(cmd); auto it = mf.variables.find(name); if (it == mf.variables.end() || !it->second.from_cmdline) { mf.variables[name] = {value, VarFlavor::Simple, false}; if (is_override || it == mf.variables.end() || !(it->second.from_cmdline || it->second.is_override)) { mf.variables[name] = {value, VarFlavor::Simple, false, is_override}; } return true; } Loading @@ -749,13 +789,14 @@ bool Parser::try_parse_assignment(const std::string& line, Makefile& mf) { if (name.empty()) return false; auto it = mf.variables.find(name); if (it != mf.variables.end() && it->second.from_cmdline) return true; if (!is_override && it != mf.variables.end() && (it->second.from_cmdline || it->second.is_override)) return true; if (flavor == VarFlavor::Simple) { value = expand(value, mf); } mf.variables[name] = {value, flavor, false}; mf.variables[name] = {value, flavor, false, is_override}; return true; } Loading Loading @@ -904,11 +945,13 @@ bool Parser::try_parse_rule(const std::string& line, Makefile& mf, Rule*& curren } else { pprereqs = split_words(ppat); } std::vector<std::string> pprereqs_raw = pprereqs; for (auto& p : pprereqs) p = normalize_target(p); for (auto& p : poo) p = normalize_target(p); Rule rule; rule.targets = targets; rule.prerequisites = pprereqs; set_raw_prereqs(rule, pprereqs_raw); rule.order_only_prereqs = poo; rule.is_pattern = false; rule.target_pattern = tpat; Loading Loading @@ -1019,6 +1062,7 @@ bool Parser::try_parse_rule(const std::string& line, Makefile& mf, Rule*& curren }; glob_words(prereqs); glob_words(order_only); std::vector<std::string> prereqs_raw = prereqs; for (auto& p : prereqs) p = normalize_target(p); for (auto& p : order_only) p = normalize_target(p); Loading Loading @@ -1093,6 +1137,7 @@ bool Parser::try_parse_rule(const std::string& line, Makefile& mf, Rule*& curren mf.rules.push_back({targets, prereqs, order_only, {}, is_pattern, ""}); current_rule = &mf.rules.back(); set_raw_prereqs(*current_rule, prereqs_raw); current_rule->double_colon = double_colon && !is_pattern; if (has_inline_recipe) { Loading Loading @@ -1997,6 +2042,7 @@ std::string Parser::expand_function(const std::string& name, const std::string& std::string var = trim(do_expand(args, mf, auto_vars)); auto it = mf.variables.find(var); if (it == mf.variables.end()) return "undefined"; if (it->second.is_override) return "override"; if (it->second.from_cmdline) return "command line"; return "file"; } Loading
parser.h +12 −1 Changes for parser.h: 12 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ struct Variable { std::string value; VarFlavor flavor = VarFlavor::Recursive; bool from_cmdline = false; // set by "override NAME op VALUE": beats the command line, and later // plain assignments in the makefile leave it alone (GNU make) bool is_override = false; }; struct Rule { Loading @@ -32,6 +35,11 @@ struct Rule { // "target :: prereqs": each double-colon rule of a target is // independent, with its own recipe run when its own prereqs require it. bool double_colon = false; // The prerequisites as written (only a leading "./" stripped), when that // differs from normalize_target()'s spelling in `prerequisites`: lookups // use the normalized names, $^/$</$+/$? the written ones like GNU make // (tools/build's "$(filter $(obj-y),$^)" with "dir//x.o" in obj-y). std::vector<std::string> prerequisites_raw = {}; }; struct Makefile { Loading Loading @@ -112,6 +120,9 @@ public: // "kernel/bounds.s". Apply this consistently to targets, prerequisites, // patterns, and requested goals. static std::string normalize_target(const std::string& s); // Only the leading "./" segments normalize_target() strips, which GNU // make strips from prerequisite names too. static std::string strip_dot_slash(const std::string& s); private: std::string error_; Loading @@ -132,7 +143,7 @@ private: // Detect 'NAME op VALUE' target-specific variable bodies after a ':'. bool parse_target_var(const std::string& rest, std::string& name, std::string& op, std::string& value) const; bool try_parse_assignment(const std::string& line, Makefile& mf); bool try_parse_assignment(const std::string& line, Makefile& mf, bool is_override = false); bool try_parse_rule(const std::string& line, Makefile& mf, Rule*& current_rule); void handle_include(const std::string& args, Makefile& mf, bool optional); bool handle_conditional(const std::string& line, Makefile& mf); Loading