diff options
| author | Dan Willemsen <dwillemsen@google.com> | 2020-06-26 19:20:26 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-06-26 19:20:26 -0700 |
| commit | 10cc982b563c19890872b73e6d8fb44aeda646ae (patch) | |
| tree | 6b5075e832cbdf2a7996a25a26659363527b6e4c /src/dep.cc | |
| parent | 003cf51e9b6da48063c90cf4c6710fde103c9c4a (diff) | |
| parent | 979e7ae6e417ae4ee45e835104b66191ae16a14c (diff) | |
| download | platform_build_kati-10cc982b563c19890872b73e6d8fb44aeda646ae.tar.gz platform_build_kati-10cc982b563c19890872b73e6d8fb44aeda646ae.tar.bz2 platform_build_kati-10cc982b563c19890872b73e6d8fb44aeda646ae.zip | |
Merge pull request #199 from danw/refactor
Refactor source tree into directories
Diffstat (limited to 'src/dep.cc')
| -rw-r--r-- | src/dep.cc | 923 |
1 files changed, 923 insertions, 0 deletions
diff --git a/src/dep.cc b/src/dep.cc new file mode 100644 index 0000000..3289d7a --- /dev/null +++ b/src/dep.cc @@ -0,0 +1,923 @@ +// Copyright 2015 Google Inc. All rights reserved +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build ignore + +#include "dep.h" + +#include <algorithm> +#include <iterator> +#include <map> +#include <memory> +#include <unordered_map> +#include <unordered_set> + +#include "eval.h" +#include "fileutil.h" +#include "flags.h" +#include "log.h" +#include "rule.h" +#include "stats.h" +#include "strutil.h" +#include "symtab.h" +#include "timeutil.h" +#include "var.h" + +namespace { + +static vector<DepNode*>* g_dep_node_pool; + +static Symbol ReplaceSuffix(Symbol s, Symbol newsuf) { + string r; + AppendString(StripExt(s.str()), &r); + r += '.'; + AppendString(newsuf.str(), &r); + return Intern(r); +} + +void ApplyOutputPattern(const Rule& r, + Symbol output, + const vector<Symbol>& inputs, + vector<Symbol>* out_inputs) { + if (inputs.empty()) + return; + if (r.is_suffix_rule) { + for (Symbol input : inputs) { + out_inputs->push_back(ReplaceSuffix(output, input)); + } + return; + } + if (r.output_patterns.empty()) { + copy(inputs.begin(), inputs.end(), back_inserter(*out_inputs)); + return; + } + CHECK(r.output_patterns.size() == 1); + Pattern pat(r.output_patterns[0].str()); + for (Symbol input : inputs) { + string buf; + pat.AppendSubst(output.str(), input.str(), &buf); + out_inputs->push_back(Intern(buf)); + } +} + +class RuleTrie { + struct Entry { + Entry(const Rule* r, StringPiece s) : rule(r), suffix(s) {} + const Rule* rule; + StringPiece suffix; + }; + + public: + RuleTrie() {} + ~RuleTrie() { + for (auto& p : children_) + delete p.second; + } + + void Add(StringPiece name, const Rule* rule) { + if (name.empty() || name[0] == '%') { + rules_.push_back(Entry(rule, name)); + return; + } + const char c = name[0]; + auto p = children_.emplace(c, nullptr); + if (p.second) { + p.first->second = new RuleTrie(); + } + p.first->second->Add(name.substr(1), rule); + } + + void Get(StringPiece name, vector<const Rule*>* rules) const { + for (const Entry& ent : rules_) { + if ((ent.suffix.empty() && name.empty()) || + HasSuffix(name, ent.suffix.substr(1))) { + rules->push_back(ent.rule); + } + } + if (name.empty()) + return; + auto found = children_.find(name[0]); + if (found != children_.end()) { + found->second->Get(name.substr(1), rules); + } + } + + size_t size() const { + size_t r = rules_.size(); + for (const auto& c : children_) + r += c.second->size(); + return r; + } + + private: + vector<Entry> rules_; + unordered_map<char, RuleTrie*> children_; +}; + +bool IsSuffixRule(Symbol output) { + if (output.empty() || !IsSpecialTarget(output)) + return false; + const StringPiece rest = StringPiece(output.str()).substr(1); + size_t dot_index = rest.find('.'); + // If there is only a single dot or the third dot, this is not a + // suffix rule. + if (dot_index == string::npos || + rest.substr(dot_index + 1).find('.') != string::npos) { + return false; + } + return true; +} + +struct RuleMerger { + vector<const Rule*> rules; + vector<pair<Symbol, RuleMerger*>> implicit_outputs; + vector<Symbol> validations; + const Rule* primary_rule; + const RuleMerger* parent; + Symbol parent_sym; + bool is_double_colon; + + RuleMerger() + : primary_rule(nullptr), parent(nullptr), is_double_colon(false) {} + + void AddImplicitOutput(Symbol output, RuleMerger* merger) { + implicit_outputs.push_back(make_pair(output, merger)); + } + + void AddValidation(Symbol validation) { validations.push_back(validation); } + + void SetImplicitOutput(Symbol output, Symbol p, const RuleMerger* merger) { + if (!merger->primary_rule) { + ERROR("*** implicit output `%s' on phony target `%s'", output.c_str(), + p.c_str()); + } + if (parent) { + ERROR_LOC(merger->primary_rule->cmd_loc(), + "*** implicit output `%s' of `%s' was already defined by `%s' " + "at %s:%d", + output.c_str(), p.c_str(), parent_sym.c_str(), + LOCF(parent->primary_rule->cmd_loc())); + } + if (primary_rule) { + ERROR_LOC(primary_rule->cmd_loc(), + "*** implicit output `%s' may not have commands", + output.c_str()); + } + parent = merger; + parent_sym = p; + } + + void AddRule(Symbol output, const Rule* r) { + if (rules.empty()) { + is_double_colon = r->is_double_colon; + } else if (is_double_colon != r->is_double_colon) { + ERROR_LOC(r->loc, "*** target file `%s' has both : and :: entries.", + output.c_str()); + } + + if (primary_rule && !r->cmds.empty() && !IsSuffixRule(output) && + !r->is_double_colon) { + if (g_flags.werror_overriding_commands) { + ERROR_LOC(r->cmd_loc(), + "*** overriding commands for target `%s', previously defined " + "at %s:%d", + output.c_str(), LOCF(primary_rule->cmd_loc())); + } else { + WARN_LOC(r->cmd_loc(), "warning: overriding commands for target `%s'", + output.c_str()); + WARN_LOC(primary_rule->cmd_loc(), + "warning: ignoring old commands for target `%s'", + output.c_str()); + } + primary_rule = r; + } + if (!primary_rule && !r->cmds.empty()) { + primary_rule = r; + } + + rules.push_back(r); + } + + void FillDepNodeFromRule(Symbol output, const Rule* r, DepNode* n) const { + if (is_double_colon) + copy(r->cmds.begin(), r->cmds.end(), back_inserter(n->cmds)); + + ApplyOutputPattern(*r, output, r->inputs, &n->actual_inputs); + ApplyOutputPattern(*r, output, r->order_only_inputs, + &n->actual_order_only_inputs); + + if (r->output_patterns.size() >= 1) { + CHECK(r->output_patterns.size() == 1); + n->output_pattern = r->output_patterns[0]; + } + } + + void FillDepNodeLoc(const Rule* r, DepNode* n) const { + n->loc = r->loc; + if (!r->cmds.empty() && r->cmd_lineno) + n->loc.lineno = r->cmd_lineno; + } + + void FillDepNode(Symbol output, const Rule* pattern_rule, DepNode* n) const { + if (primary_rule) { + CHECK(!pattern_rule); + FillDepNodeFromRule(output, primary_rule, n); + FillDepNodeLoc(primary_rule, n); + n->cmds = primary_rule->cmds; + } else if (pattern_rule) { + FillDepNodeFromRule(output, pattern_rule, n); + FillDepNodeLoc(pattern_rule, n); + n->cmds = pattern_rule->cmds; + } + + for (const Rule* r : rules) { + if (r == primary_rule) + continue; + FillDepNodeFromRule(output, r, n); + if (n->loc.filename == NULL) + n->loc = r->loc; + } + + for (auto& implicit_output : implicit_outputs) { + n->implicit_outputs.push_back(implicit_output.first); + for (const Rule* r : implicit_output.second->rules) { + FillDepNodeFromRule(output, r, n); + } + } + + for (auto& validation : validations) { + n->actual_validations.push_back(validation); + } + } +}; + +} // namespace + +DepNode::DepNode(Symbol o, bool p, bool r) + : output(o), + has_rule(false), + is_default_target(false), + is_phony(p), + is_restat(r), + rule_vars(NULL), + depfile_var(NULL), + ninja_pool_var(NULL) { + g_dep_node_pool->push_back(this); +} + +class DepBuilder { + public: + DepBuilder(Evaluator* ev, + const vector<const Rule*>& rules, + const unordered_map<Symbol, Vars*>& rule_vars) + : ev_(ev), + rule_vars_(rule_vars), + implicit_rules_(new RuleTrie()), + depfile_var_name_(Intern(".KATI_DEPFILE")), + implicit_outputs_var_name_(Intern(".KATI_IMPLICIT_OUTPUTS")), + ninja_pool_var_name_(Intern(".KATI_NINJA_POOL")), + validations_var_name_(Intern(".KATI_VALIDATIONS")) { + ScopedTimeReporter tr("make dep (populate)"); + PopulateRules(rules); + // TODO? + // LOG_STAT("%zu variables", ev->mutable_vars()->size()); + LOG_STAT("%zu explicit rules", rules_.size()); + LOG_STAT("%zu implicit rules", implicit_rules_->size()); + LOG_STAT("%zu suffix rules", suffix_rules_.size()); + + HandleSpecialTargets(); + } + + void HandleSpecialTargets() { + Loc loc; + vector<Symbol> targets; + + if (GetRuleInputs(Intern(".PHONY"), &targets, &loc)) { + for (Symbol t : targets) + phony_.insert(t); + } + if (GetRuleInputs(Intern(".KATI_RESTAT"), &targets, &loc)) { + for (Symbol t : targets) + restat_.insert(t); + } + if (GetRuleInputs(Intern(".SUFFIXES"), &targets, &loc)) { + if (targets.empty()) { + suffix_rules_.clear(); + } else { + WARN_LOC(loc, "kati doesn't support .SUFFIXES with prerequisites"); + } + } + + // Note we can safely ignore .DELETE_ON_ERROR for --ninja mode. + static const char* kUnsupportedBuiltinTargets[] = {".DEFAULT", + ".PRECIOUS", + ".INTERMEDIATE", + ".SECONDARY", + ".SECONDEXPANSION", + ".IGNORE", + ".LOW_RESOLUTION_TIME", + ".SILENT", + ".EXPORT_ALL_VARIABLES", + ".NOTPARALLEL", + ".ONESHELL", + NULL}; + for (const char** p = kUnsupportedBuiltinTargets; *p; p++) { + if (GetRuleInputs(Intern(*p), &targets, &loc)) { + WARN_LOC(loc, "kati doesn't support %s", *p); + } + } + } + + ~DepBuilder() {} + + void Build(vector<Symbol> targets, vector<NamedDepNode>* nodes) { + if (!first_rule_.IsValid()) { + ERROR("*** No targets."); + } + + if (!g_flags.gen_all_targets && targets.empty()) { + targets.push_back(first_rule_); + } + if (g_flags.gen_all_targets) { + SymbolSet non_root_targets; + for (const auto& p : rules_) { + if (IsSpecialTarget(p.first)) + continue; + for (const Rule* r : p.second.rules) { + for (Symbol t : r->inputs) + non_root_targets.insert(t); + for (Symbol t : r->order_only_inputs) + non_root_targets.insert(t); + } + } + + for (const auto& p : rules_) { + Symbol t = p.first; + if (!non_root_targets.exists(t) && !IsSpecialTarget(t)) { + targets.push_back(p.first); + } + } + } + + // TODO: LogStats? + + for (Symbol target : targets) { + cur_rule_vars_.reset(new Vars); + ev_->set_current_scope(cur_rule_vars_.get()); + DepNode* n = BuildPlan(target, Intern("")); + nodes->push_back({target, n}); + ev_->set_current_scope(NULL); + cur_rule_vars_.reset(NULL); + } + } + + private: + bool Exists(Symbol target) { + return (rules_.find(target) != rules_.end()) || phony_.exists(target) || + ::Exists(target.str()); + } + + bool GetRuleInputs(Symbol s, vector<Symbol>* o, Loc* l) { + auto found = rules_.find(s); + if (found == rules_.end()) + return false; + + o->clear(); + CHECK(!found->second.rules.empty()); + *l = found->second.rules.front()->loc; + for (const Rule* r : found->second.rules) { + for (Symbol i : r->inputs) + o->push_back(i); + } + return true; + } + + void PopulateRules(const vector<const Rule*>& rules) { + for (const Rule* rule : rules) { + if (rule->outputs.empty()) { + PopulateImplicitRule(rule); + } else { + PopulateExplicitRule(rule); + } + } + for (auto& p : suffix_rules_) { + reverse(p.second.begin(), p.second.end()); + } + for (auto& p : rules_) { + auto vars = LookupRuleVars(p.first); + if (!vars) { + continue; + } + auto var = vars->Lookup(implicit_outputs_var_name_); + if (var->IsDefined()) { + string implicit_outputs; + var->Eval(ev_, &implicit_outputs); + + for (StringPiece output : WordScanner(implicit_outputs)) { + Symbol sym = Intern(TrimLeadingCurdir(output)); + rules_[sym].SetImplicitOutput(sym, p.first, &p.second); + p.second.AddImplicitOutput(sym, &rules_[sym]); + } + } + + var = vars->Lookup(validations_var_name_); + if (var->IsDefined()) { + string validations; + var->Eval(ev_, &validations); + + for (StringPiece validation : WordScanner(validations)) { + Symbol sym = Intern(TrimLeadingCurdir(validation)); + p.second.AddValidation(sym); + } + } + } + } + + bool PopulateSuffixRule(const Rule* rule, Symbol output) { + if (!IsSuffixRule(output)) + return false; + + if (g_flags.werror_suffix_rules) { + ERROR_LOC(rule->loc, "*** suffix rules are obsolete: %s", output.c_str()); + } else if (g_flags.warn_suffix_rules) { + WARN_LOC(rule->loc, "warning: suffix rules are deprecated: %s", + output.c_str()); + } + + const StringPiece rest = StringPiece(output.str()).substr(1); + size_t dot_index = rest.find('.'); + + StringPiece input_suffix = rest.substr(0, dot_index); + StringPiece output_suffix = rest.substr(dot_index + 1); + shared_ptr<Rule> r = make_shared<Rule>(*rule); + r->inputs.clear(); + r->inputs.push_back(Intern(input_suffix)); + r->is_suffix_rule = true; + suffix_rules_[output_suffix].push_back(r); + return true; + } + + void PopulateExplicitRule(const Rule* rule) { + for (Symbol output : rule->outputs) { + if (!first_rule_.IsValid() && !IsSpecialTarget(output)) { + first_rule_ = output; + } + rules_[output].AddRule(output, rule); + PopulateSuffixRule(rule, output); + } + } + + static bool IsIgnorableImplicitRule(const Rule* rule) { + // As kati doesn't have RCS/SCCS related default rules, we can + // safely ignore suppression for them. + if (rule->inputs.size() != 1) + return false; + if (!rule->order_only_inputs.empty()) + return false; + if (!rule->cmds.empty()) + return false; + const string& i = rule->inputs[0].str(); + return (i == "RCS/%,v" || i == "RCS/%" || i == "%,v" || i == "s.%" || + i == "SCCS/s.%"); + } + + void PopulateImplicitRule(const Rule* rule) { + for (Symbol output_pattern : rule->output_patterns) { + if (output_pattern.str() != "%" || !IsIgnorableImplicitRule(rule)) { + if (g_flags.werror_implicit_rules) { + ERROR_LOC(rule->loc, "*** implicit rules are obsolete: %s", + output_pattern.c_str()); + } else if (g_flags.warn_implicit_rules) { + WARN_LOC(rule->loc, "warning: implicit rules are deprecated: %s", + output_pattern.c_str()); + } + + implicit_rules_->Add(output_pattern.str(), rule); + } + } + } + + const RuleMerger* LookupRuleMerger(Symbol o) { + auto found = rules_.find(o); + if (found != rules_.end()) { + return &found->second; + } + return nullptr; + } + + Vars* LookupRuleVars(Symbol o) { + auto found = rule_vars_.find(o); + if (found != rule_vars_.end()) + return found->second; + return nullptr; + } + + bool CanPickImplicitRule(const Rule* rule, + Symbol output, + DepNode* n, + shared_ptr<Rule>* out_rule) { + Symbol matched; + for (Symbol output_pattern : rule->output_patterns) { + Pattern pat(output_pattern.str()); + if (pat.Match(output.str())) { + bool ok = true; + for (Symbol input : rule->inputs) { + string buf; + pat.AppendSubst(output.str(), input.str(), &buf); + if (!Exists(Intern(buf))) { + ok = false; + break; + } + } + + if (ok) { + matched = output_pattern; + break; + } + } + } + if (!matched.IsValid()) + return false; + + *out_rule = make_shared<Rule>(*rule); + if ((*out_rule)->output_patterns.size() > 1) { + // We should mark all other output patterns as used. + Pattern pat(matched.str()); + for (Symbol output_pattern : rule->output_patterns) { + if (output_pattern == matched) + continue; + string buf; + pat.AppendSubst(output.str(), output_pattern.str(), &buf); + done_[Intern(buf)] = n; + } + (*out_rule)->output_patterns.clear(); + (*out_rule)->output_patterns.push_back(matched); + } + + return true; + } + + Vars* MergeImplicitRuleVars(Symbol output, Vars* vars) { + auto found = rule_vars_.find(output); + if (found == rule_vars_.end()) + return vars; + if (vars == NULL) + return found->second; + // TODO: leak. + Vars* r = new Vars(*found->second); + for (auto p : *vars) { + (*r)[p.first] = p.second; + } + return r; + } + + bool PickRule(Symbol output, + DepNode* n, + const RuleMerger** out_rule_merger, + shared_ptr<Rule>* pattern_rule, + Vars** out_var) { + const RuleMerger* rule_merger = LookupRuleMerger(output); + Vars* vars = LookupRuleVars(output); + *out_rule_merger = rule_merger; + *out_var = vars; + if (rule_merger && rule_merger->primary_rule) { + for (auto implicit_output : rule_merger->implicit_outputs) { + vars = MergeImplicitRuleVars(implicit_output.first, vars); + } + *out_var = vars; + return true; + } + + vector<const Rule*> irules; + implicit_rules_->Get(output.str(), &irules); + for (auto iter = irules.rbegin(); iter != irules.rend(); ++iter) { + if (!CanPickImplicitRule(*iter, output, n, pattern_rule)) + continue; + if (rule_merger) { + return true; + } + CHECK((*pattern_rule)->output_patterns.size() == 1); + vars = MergeImplicitRuleVars((*pattern_rule)->output_patterns[0], vars); + *out_var = vars; + return true; + } + + StringPiece output_suffix = GetExt(output.str()); + if (output_suffix.get(0) != '.') + return rule_merger; + output_suffix = output_suffix.substr(1); + + SuffixRuleMap::const_iterator found = suffix_rules_.find(output_suffix); + if (found == suffix_rules_.end()) + return rule_merger; + + for (const shared_ptr<Rule>& irule : found->second) { + CHECK(irule->inputs.size() == 1); + Symbol input = ReplaceSuffix(output, irule->inputs[0]); + if (!Exists(input)) + continue; + + *pattern_rule = irule; + if (rule_merger) + return true; + if (vars) { + CHECK(irule->outputs.size() == 1); + vars = MergeImplicitRuleVars(irule->outputs[0], vars); + *out_var = vars; + } + return true; + } + + return rule_merger; + } + + DepNode* BuildPlan(Symbol output, Symbol needed_by UNUSED) { + LOG("BuildPlan: %s for %s", output.c_str(), needed_by.c_str()); + + auto found = done_.find(output); + if (found != done_.end()) { + return found->second; + } + + DepNode* n = + new DepNode(output, phony_.exists(output), restat_.exists(output)); + done_[output] = n; + + const RuleMerger* rule_merger = nullptr; + shared_ptr<Rule> pattern_rule; + Vars* vars; + if (!PickRule(output, n, &rule_merger, &pattern_rule, &vars)) { + return n; + } + if (rule_merger && rule_merger->parent) { + output = rule_merger->parent_sym; + done_[output] = n; + n->output = output; + if (!PickRule(output, n, &rule_merger, &pattern_rule, &vars)) { + return n; + } + } + if (rule_merger) + rule_merger->FillDepNode(output, pattern_rule.get(), n); + else + RuleMerger().FillDepNode(output, pattern_rule.get(), n); + + vector<unique_ptr<ScopedVar>> sv; + if (vars) { + for (const auto& p : *vars) { + Symbol name = p.first; + Var* var = p.second; + CHECK(var); + Var* new_var = var; + if (var->op() == AssignOp::PLUS_EQ) { + Var* old_var = ev_->LookupVar(name); + if (old_var->IsDefined()) { + // TODO: This would be incorrect and has a leak. + shared_ptr<string> s = make_shared<string>(); + old_var->Eval(ev_, s.get()); + if (!s->empty()) + *s += ' '; + new_var->Eval(ev_, s.get()); + new_var = new SimpleVar(*s, old_var->Origin()); + } + } else if (var->op() == AssignOp::QUESTION_EQ) { + Var* old_var = ev_->LookupVar(name); + if (old_var->IsDefined()) { + continue; + } + } + + if (name == depfile_var_name_) { + n->depfile_var = new_var; + } else if (name == implicit_outputs_var_name_) { + } else if (name == validations_var_name_) { + } else if (name == ninja_pool_var_name_) { + n->ninja_pool_var = new_var; + } else { + sv.emplace_back(new ScopedVar(cur_rule_vars_.get(), name, new_var)); + } + } + } + + if (g_flags.warn_phony_looks_real && n->is_phony && + output.str().find("/") != string::npos) { + if (g_flags.werror_phony_looks_real) { + ERROR_LOC( + n->loc, + "*** PHONY target \"%s\" looks like a real file (contains a \"/\")", + output.c_str()); + } else { + WARN_LOC(n->loc, + "warning: PHONY target \"%s\" looks like a real file " + "(contains a \"/\")", + output.c_str()); + } + } + + if (!g_flags.writable.empty() && !n->is_phony) { + bool found = false; + for (const auto& w : g_flags.writable) { + if (StringPiece(output.str()).starts_with(w)) { + found = true; + break; + } + } + if (!found) { + if (g_flags.werror_writable) { + ERROR_LOC(n->loc, "*** writing to readonly directory: \"%s\"", + output.c_str()); + } else { + WARN_LOC(n->loc, "warning: writing to readonly directory: \"%s\"", + output.c_str()); + } + } + } + + for (Symbol output : n->implicit_outputs) { + done_[output] = n; + + if (g_flags.warn_phony_looks_real && n->is_phony && + output.str().find("/") != string::npos) { + if (g_flags.werror_phony_looks_real) { + ERROR_LOC(n->loc, + "*** PHONY target \"%s\" looks like a real file (contains " + "a \"/\")", + output.c_str()); + } else { + WARN_LOC(n->loc, + "warning: PHONY target \"%s\" looks like a real file " + "(contains a \"/\")", + output.c_str()); + } + } + + if (!g_flags.writable.empty() && !n->is_phony) { + bool found = false; + for (const auto& w : g_flags.writable) { + if (StringPiece(output.str()).starts_with(w)) { + found = true; + break; + } + } + if (!found) { + if (g_flags.werror_writable) { + ERROR_LOC(n->loc, "*** writing to readonly directory: \"%s\"", + output.c_str()); + } else { + WARN_LOC(n->loc, "warning: writing to readonly directory: \"%s\"", + output.c_str()); + } + } + } + } + + for (Symbol input : n->actual_inputs) { + DepNode* c = BuildPlan(input, output); + n->deps.push_back({input, c}); + + bool is_phony = c->is_phony; + if (!is_phony && !c->has_rule && g_flags.top_level_phony) { + is_phony = input.str().find("/") == string::npos; + } + if (!n->is_phony && is_phony) { + if (g_flags.werror_real_to_phony) { + ERROR_LOC(n->loc, + "*** real file \"%s\" depends on PHONY target \"%s\"", + output.c_str(), input.c_str()); + } else if (g_flags.warn_real_to_phony) { + WARN_LOC(n->loc, + "warning: real file \"%s\" depends on PHONY target \"%s\"", + output.c_str(), input.c_str()); + } + } + } + + for (Symbol input : n->actual_order_only_inputs) { + DepNode* c = BuildPlan(input, output); + n->order_onlys.push_back({input, c}); + } + + for (Symbol validation : n->actual_validations) { + if (!g_flags.use_ninja_validations) { + ERROR_LOC( + n->loc, + ".KATI_VALIDATIONS not allowed without --use_ninja_validations"); + } + DepNode* c = BuildPlan(validation, output); + n->validations.push_back({validation, c}); + } + + // Block on werror_writable/werror_phony_looks_real, because otherwise we + // can't rely on is_phony being valid for this check. + if (!n->is_phony && n->cmds.empty() && g_flags.werror_writable && + g_flags.werror_phony_looks_real) { + if (n->deps.empty() && n->order_onlys.empty()) { + if (g_flags.werror_real_no_cmds_or_deps) { + ERROR_LOC( + n->loc, + "*** target \"%s\" has no commands or deps that could create it", + output.c_str()); + } else if (g_flags.warn_real_no_cmds_or_deps) { + WARN_LOC(n->loc, + "warning: target \"%s\" has no commands or deps that could " + "create it", + output.c_str()); + } + } else { + if (n->actual_inputs.size() == 1) { + if (g_flags.werror_real_no_cmds) { + ERROR_LOC(n->loc, + "*** target \"%s\" has no commands. Should \"%s\" be " + "using .KATI_IMPLICIT_OUTPUTS?", + output.c_str(), n->actual_inputs[0].c_str()); + } else if (g_flags.warn_real_no_cmds) { + WARN_LOC(n->loc, + "warning: target \"%s\" has no commands. Should \"%s\" be " + "using .KATI_IMPLICIT_OUTPUTS?", + output.c_str(), n->actual_inputs[0].c_str()); + } + } else { + if (g_flags.werror_real_no_cmds) { + ERROR_LOC( + n->loc, + "*** target \"%s\" has no commands that could create output " + "file. Is a dependency missing .KATI_IMPLICIT_OUTPUTS?", + output.c_str()); + } else if (g_flags.warn_real_no_cmds) { + WARN_LOC( + n->loc, + "warning: target \"%s\" has no commands that could create " + "output file. Is a dependency missing .KATI_IMPLICIT_OUTPUTS?", + output.c_str()); + } + } + } + } + + n->has_rule = true; + n->is_default_target = first_rule_ == output; + if (cur_rule_vars_->empty()) { + n->rule_vars = NULL; + } else { + n->rule_vars = new Vars; + for (auto p : *cur_rule_vars_) { + n->rule_vars->insert(p); + } + } + + return n; + } + + Evaluator* ev_; + map<Symbol, RuleMerger> rules_; + const unordered_map<Symbol, Vars*>& rule_vars_; + unique_ptr<Vars> cur_rule_vars_; + + unique_ptr<RuleTrie> implicit_rules_; + typedef unordered_map<StringPiece, vector<shared_ptr<Rule>>> SuffixRuleMap; + SuffixRuleMap suffix_rules_; + + Symbol first_rule_; + unordered_map<Symbol, DepNode*> done_; + SymbolSet phony_; + SymbolSet restat_; + Symbol depfile_var_name_; + Symbol implicit_outputs_var_name_; + Symbol ninja_pool_var_name_; + Symbol validations_var_name_; +}; + +void MakeDep(Evaluator* ev, + const vector<const Rule*>& rules, + const unordered_map<Symbol, Vars*>& rule_vars, + const vector<Symbol>& targets, + vector<NamedDepNode>* nodes) { + DepBuilder db(ev, rules, rule_vars); + ScopedTimeReporter tr("make dep (build)"); + db.Build(targets, nodes); +} + +void InitDepNodePool() { + g_dep_node_pool = new vector<DepNode*>; +} + +void QuitDepNodePool() { + for (DepNode* n : *g_dep_node_pool) + delete n; + delete g_dep_node_pool; +} + +bool IsSpecialTarget(Symbol output) { + return output.get(0) == '.' && output.get(1) != '.'; +} |
