Loading executor.cpp +15 −18 Changes for executor.cpp: 15 added lines, 18 removed lines. Original line number Diff line number Diff line Loading @@ -520,14 +520,14 @@ std::map<std::string, std::string> Executor::compute_target_scope( (suffix.empty() || tgt.substr(tgt.size() - suffix.size()) == suffix); }; // Build an expansion context = global variables overlaid with the current // scope, so a binding value like "$(KBUILD_AFLAGS_32)" or one referencing // an inherited override resolves correctly. auto resolve = [&](const std::string& raw) { Makefile ctx = mf_; for (const auto& kv : scope) ctx.variables[kv.first] = {kv.second, VarFlavor::Simple, false}; return parser_.expand(raw, ctx); // Scope values are kept unexpanded: execute_rule installs them as // recursive variables, so they are expanded in the recipe, with $@ set // and the whole scope (e.g. an inherited override) in effect -- as GNU // make does. Expanding here would see an empty $@. Values that were // already expanded (':=' bindings, simple globals) are escaped so the // recipe-time expansion reproduces them unchanged. auto unexpanded = [](const Makefile::TargetVar& b) { return b.recursive ? b.value : Parser::escape_dollars(b.value); }; for (const auto& entry : mf_.target_vars) { Loading @@ -554,7 +554,7 @@ std::map<std::string, std::string> Executor::compute_target_scope( if (git != mf_.variables.end() && git->second.from_cmdline) continue; } std::string val = resolve(b.value); std::string val = unexpanded(b); if (b.append) { std::string base; auto sit = scope.find(b.name); Loading @@ -563,16 +563,13 @@ std::map<std::string, std::string> Executor::compute_target_scope( } else { auto git = mf_.variables.find(b.name); if (git != mf_.variables.end()) // Scope-aware, like `resolve` above: a recursively- // expanded base value may itself reference a variable // that the current target scope overrides (e.g. // zstd's "CFLAGS += $(DEBUGFLAGS) ..." where a // '%-release' pattern binds DEBUGFLAGS := "" for this // target). Expanding against the un-scoped mf_ would // resolve $(DEBUGFLAGS) to its global default instead // of the target-specific override in effect here. // Left unexpanded too: a recursively-expanded base // may reference a variable the target scope overrides // (e.g. zstd's "CFLAGS += $(DEBUGFLAGS) ..." where a // '%-release' pattern binds DEBUGFLAGS := ""), which // must resolve to the override, not the global value. base = (git->second.flavor == VarFlavor::Recursive) ? resolve(git->second.value) : git->second.value; ? git->second.value : Parser::escape_dollars(git->second.value); } scope[b.name] = base.empty() ? val : base + " " + val; } else { Loading parser.cpp +18 −1 Changes for parser.cpp: 18 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -21,6 +21,16 @@ std::string Parser::trim(const std::string& s) { return s.substr(start, end - start + 1); } std::string Parser::escape_dollars(const std::string& s) { std::string out; out.reserve(s.size()); for (char c : s) { if (c == '$') out += '$'; out += c; } return out; } std::vector<std::string> Parser::split_words(const std::string& s) { std::vector<std::string> result; std::istringstream iss(s); Loading Loading @@ -940,10 +950,17 @@ bool Parser::try_parse_rule(const std::string& line, Makefile& mf, Rule*& curren for (auto& pr : vec) { if (pr.name == tv_name) { if (tv_op == "+=") { // Appending to an already expanded ':=' binding: // keep the merged text unexpanded-safe. if (!pr.recursive) { pr.value = escape_dollars(pr.value); pr.recursive = true; } pr.value = pr.value.empty() ? val : pr.value + " " + val; } else { pr.value = val; pr.append = false; pr.recursive = tv_op != ":="; } // Sticky: once any binding for this (target, name) // pair used 'override', treat the merged binding as Loading @@ -955,7 +972,7 @@ bool Parser::try_parse_rule(const std::string& line, Makefile& mf, Rule*& curren } // A first-time '+=' must append to whatever value the variable // has in the surrounding (global) scope when the recipe runs. if (!found) vec.push_back({tv_name, val, tv_op == "+=", tv_override}); if (!found) vec.push_back({tv_name, val, tv_op == "+=", tv_override, tv_op != ":="}); } current_rule = nullptr; return true; Loading parser.h +9 −0 Changes for parser.h: 9 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -62,6 +62,12 @@ struct Makefile { // make silently leaves a command-line-origin variable untouched // (see compute_target_scope). bool is_override = false; // Whether `value` is still unexpanded ('=', '+=', '?=') rather than // already expanded at parse time (':='). Recursive bindings must be // expanded only in the recipe, where $@ and friends are set: Kbuild's // '$(obj)/%.o: private objtool-enabled = $(if $(is-standard-object),...)' // depends on $@ and is empty when expanded any earlier. bool recursive = true; }; std::map<std::string, std::vector<TargetVar>> target_vars; // Variables marked for export into the recipe / sub-make environment. Loading Loading @@ -95,6 +101,9 @@ public: const std::string& error() const { return error_; } static std::string trim(const std::string& s); // "$" -> "$$": turns an already expanded value into text that expands // back to itself, for storing it where values are expanded again. static std::string escape_dollars(const std::string& s); static std::vector<std::string> split_words(const std::string& s); static bool glob_match(const std::string& pattern, const std::string& str); // GNU make strips a leading "./" from file/target/prereq names so that Loading Loading
executor.cpp +15 −18 Changes for executor.cpp: 15 added lines, 18 removed lines. Original line number Diff line number Diff line Loading @@ -520,14 +520,14 @@ std::map<std::string, std::string> Executor::compute_target_scope( (suffix.empty() || tgt.substr(tgt.size() - suffix.size()) == suffix); }; // Build an expansion context = global variables overlaid with the current // scope, so a binding value like "$(KBUILD_AFLAGS_32)" or one referencing // an inherited override resolves correctly. auto resolve = [&](const std::string& raw) { Makefile ctx = mf_; for (const auto& kv : scope) ctx.variables[kv.first] = {kv.second, VarFlavor::Simple, false}; return parser_.expand(raw, ctx); // Scope values are kept unexpanded: execute_rule installs them as // recursive variables, so they are expanded in the recipe, with $@ set // and the whole scope (e.g. an inherited override) in effect -- as GNU // make does. Expanding here would see an empty $@. Values that were // already expanded (':=' bindings, simple globals) are escaped so the // recipe-time expansion reproduces them unchanged. auto unexpanded = [](const Makefile::TargetVar& b) { return b.recursive ? b.value : Parser::escape_dollars(b.value); }; for (const auto& entry : mf_.target_vars) { Loading @@ -554,7 +554,7 @@ std::map<std::string, std::string> Executor::compute_target_scope( if (git != mf_.variables.end() && git->second.from_cmdline) continue; } std::string val = resolve(b.value); std::string val = unexpanded(b); if (b.append) { std::string base; auto sit = scope.find(b.name); Loading @@ -563,16 +563,13 @@ std::map<std::string, std::string> Executor::compute_target_scope( } else { auto git = mf_.variables.find(b.name); if (git != mf_.variables.end()) // Scope-aware, like `resolve` above: a recursively- // expanded base value may itself reference a variable // that the current target scope overrides (e.g. // zstd's "CFLAGS += $(DEBUGFLAGS) ..." where a // '%-release' pattern binds DEBUGFLAGS := "" for this // target). Expanding against the un-scoped mf_ would // resolve $(DEBUGFLAGS) to its global default instead // of the target-specific override in effect here. // Left unexpanded too: a recursively-expanded base // may reference a variable the target scope overrides // (e.g. zstd's "CFLAGS += $(DEBUGFLAGS) ..." where a // '%-release' pattern binds DEBUGFLAGS := ""), which // must resolve to the override, not the global value. base = (git->second.flavor == VarFlavor::Recursive) ? resolve(git->second.value) : git->second.value; ? git->second.value : Parser::escape_dollars(git->second.value); } scope[b.name] = base.empty() ? val : base + " " + val; } else { Loading
parser.cpp +18 −1 Changes for parser.cpp: 18 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -21,6 +21,16 @@ std::string Parser::trim(const std::string& s) { return s.substr(start, end - start + 1); } std::string Parser::escape_dollars(const std::string& s) { std::string out; out.reserve(s.size()); for (char c : s) { if (c == '$') out += '$'; out += c; } return out; } std::vector<std::string> Parser::split_words(const std::string& s) { std::vector<std::string> result; std::istringstream iss(s); Loading Loading @@ -940,10 +950,17 @@ bool Parser::try_parse_rule(const std::string& line, Makefile& mf, Rule*& curren for (auto& pr : vec) { if (pr.name == tv_name) { if (tv_op == "+=") { // Appending to an already expanded ':=' binding: // keep the merged text unexpanded-safe. if (!pr.recursive) { pr.value = escape_dollars(pr.value); pr.recursive = true; } pr.value = pr.value.empty() ? val : pr.value + " " + val; } else { pr.value = val; pr.append = false; pr.recursive = tv_op != ":="; } // Sticky: once any binding for this (target, name) // pair used 'override', treat the merged binding as Loading @@ -955,7 +972,7 @@ bool Parser::try_parse_rule(const std::string& line, Makefile& mf, Rule*& curren } // A first-time '+=' must append to whatever value the variable // has in the surrounding (global) scope when the recipe runs. if (!found) vec.push_back({tv_name, val, tv_op == "+=", tv_override}); if (!found) vec.push_back({tv_name, val, tv_op == "+=", tv_override, tv_op != ":="}); } current_rule = nullptr; return true; Loading
parser.h +9 −0 Changes for parser.h: 9 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -62,6 +62,12 @@ struct Makefile { // make silently leaves a command-line-origin variable untouched // (see compute_target_scope). bool is_override = false; // Whether `value` is still unexpanded ('=', '+=', '?=') rather than // already expanded at parse time (':='). Recursive bindings must be // expanded only in the recipe, where $@ and friends are set: Kbuild's // '$(obj)/%.o: private objtool-enabled = $(if $(is-standard-object),...)' // depends on $@ and is empty when expanded any earlier. bool recursive = true; }; std::map<std::string, std::vector<TargetVar>> target_vars; // Variables marked for export into the recipe / sub-make environment. Loading Loading @@ -95,6 +101,9 @@ public: const std::string& error() const { return error_; } static std::string trim(const std::string& s); // "$" -> "$$": turns an already expanded value into text that expands // back to itself, for storing it where values are expanded again. static std::string escape_dollars(const std::string& s); static std::vector<std::string> split_words(const std::string& s); static bool glob_match(const std::string& pattern, const std::string& str); // GNU make strips a leading "./" from file/target/prereq names so that Loading