1 // Copyright 2015 Google Inc. All rights reserved 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build ignore 16 17 #include "rule.h" 18 19 #include "expr.h" 20 #include "log.h" 21 #include "parser.h" 22 #include "stringprintf.h" 23 #include "strutil.h" 24 #include "symtab.h" 25 26 namespace { 27 28 static void ParseInputs(Rule* r, StringPiece s) { 29 bool is_order_only = false; 30 for (StringPiece input : WordScanner(s)) { 31 if (input == "|") { 32 is_order_only = true; 33 continue; 34 } 35 Symbol input_sym = Intern(TrimLeadingCurdir(input)); 36 if (is_order_only) { 37 r->order_only_inputs.push_back(input_sym); 38 } else { 39 r->inputs.push_back(input_sym); 40 } 41 } 42 } 43 44 bool IsPatternRule(StringPiece s) { 45 return s.find('%') != string::npos; 46 } 47 48 } // namespace 49 50 Rule::Rule() 51 : is_double_colon(false), 52 is_suffix_rule(false), 53 cmd_lineno(0) { 54 } 55 56 void ParseRule(Loc& loc, StringPiece line, char term, 57 const function<string()> &after_term_fn, 58 Rule** out_rule, RuleVarAssignment* rule_var) { 59 size_t index = line.find(':'); 60 if (index == string::npos) { 61 ERROR_LOC(loc, "*** missing separator."); 62 } 63 64 StringPiece first = line.substr(0, index); 65 vector<Symbol> outputs; 66 for (StringPiece tok : WordScanner(first)) { 67 outputs.push_back(Intern(TrimLeadingCurdir(tok))); 68 } 69 70 const bool is_first_pattern = ( 71 !outputs.empty() && IsPatternRule(outputs[0].str())); 72 for (size_t i = 1; i < outputs.size(); i++) { 73 if (IsPatternRule(outputs[i].str()) != is_first_pattern) { 74 ERROR_LOC(loc, "*** mixed implicit and normal rules: deprecated syntax"); 75 } 76 } 77 78 bool is_double_colon = false; 79 index++; 80 if (line.get(index) == ':') { 81 is_double_colon = true; 82 index++; 83 } 84 85 StringPiece rest = line.substr(index); 86 size_t term_index = rest.find_first_of("=;"); 87 string buf; 88 if ((term_index != string::npos && rest[term_index] == '=') || 89 (term_index == string::npos && term == '=')) { 90 if (term_index == string::npos) 91 term_index = rest.size(); 92 // "test: =foo" is questionable but a valid rule definition (not a 93 // target specific variable). 94 // See https://github.com/google/kati/issues/83 95 if (term_index == 0) { 96 KATI_WARN_LOC(loc, "defining a target which starts with `=', " 97 "which is not probably what you meant"); 98 buf = line.as_string(); 99 if (term) 100 buf += term; 101 buf += after_term_fn(); 102 line = buf; 103 rest = line.substr(index); 104 term_index = string::npos; 105 } else { 106 rule_var->outputs.swap(outputs); 107 ParseAssignStatement(rest, term_index, 108 &rule_var->lhs, &rule_var->rhs, &rule_var->op); 109 *out_rule = NULL; 110 return; 111 } 112 } 113 114 Rule* rule = new Rule(); 115 *out_rule = rule; 116 rule->loc = loc; 117 rule->is_double_colon = is_double_colon; 118 if (is_first_pattern) { 119 rule->output_patterns.swap(outputs); 120 } else { 121 rule->outputs.swap(outputs); 122 } 123 if (term_index != string::npos && term != ';') { 124 CHECK(rest[term_index] == ';'); 125 // TODO: Maybe better to avoid Intern here? 126 rule->cmds.push_back( 127 NewLiteral(Intern(TrimLeftSpace(rest.substr(term_index + 1))).str())); 128 rest = rest.substr(0, term_index); 129 } 130 131 index = rest.find(':'); 132 if (index == string::npos) { 133 ParseInputs(rule, rest); 134 return; 135 } 136 137 if (is_first_pattern) { 138 ERROR_LOC(loc, "*** mixed implicit and normal rules: deprecated syntax"); 139 } 140 141 StringPiece second = rest.substr(0, index); 142 StringPiece third = rest.substr(index+1); 143 144 for (StringPiece tok : WordScanner(second)) { 145 tok = TrimLeadingCurdir(tok); 146 for (Symbol output : rule->outputs) { 147 if (!Pattern(tok).Match(output.str())) { 148 WARN_LOC(loc, "target `%s' doesn't match the target pattern", 149 output.c_str()); 150 } 151 } 152 153 rule->output_patterns.push_back(Intern(tok)); 154 } 155 156 if (rule->output_patterns.empty()) { 157 ERROR_LOC(loc, "*** missing target pattern."); 158 } 159 if (rule->output_patterns.size() > 1) { 160 ERROR_LOC(loc, "*** multiple target patterns."); 161 } 162 if (!IsPatternRule(rule->output_patterns[0].str())) { 163 ERROR_LOC(loc, "*** target pattern contains no '%%'."); 164 } 165 ParseInputs(rule, third); 166 } 167 168 string Rule::DebugString() const { 169 vector<string> v; 170 v.push_back(StringPrintf("outputs=[%s]", JoinSymbols(outputs, ",").c_str())); 171 v.push_back(StringPrintf("inputs=[%s]", JoinSymbols(inputs, ",").c_str())); 172 if (!order_only_inputs.empty()) { 173 v.push_back(StringPrintf("order_only_inputs=[%s]", 174 JoinSymbols(order_only_inputs, ",").c_str())); 175 } 176 if (!output_patterns.empty()) { 177 v.push_back(StringPrintf("output_patterns=[%s]", 178 JoinSymbols(output_patterns, ",").c_str())); 179 } 180 if (is_double_colon) 181 v.push_back("is_double_colon"); 182 if (is_suffix_rule) 183 v.push_back("is_suffix_rule"); 184 if (!cmds.empty()) { 185 v.push_back(StringPrintf("cmds=[%s]", JoinValues(cmds, ",").c_str())); 186 } 187 return JoinStrings(v, " "); 188 } 189