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 Rule** out_rule, RuleVarAssignment* rule_var) { 58 size_t index = line.find(':'); 59 if (index == string::npos) { 60 ERROR("%s:%d: *** missing separator.", LOCF(loc)); 61 } 62 63 StringPiece first = line.substr(0, index); 64 vector<Symbol> outputs; 65 for (StringPiece tok : WordScanner(first)) { 66 outputs.push_back(Intern(TrimLeadingCurdir(tok))); 67 } 68 69 const bool is_first_pattern = ( 70 !outputs.empty() && IsPatternRule(outputs[0].str())); 71 for (size_t i = 1; i < outputs.size(); i++) { 72 if (IsPatternRule(outputs[i].str()) != is_first_pattern) { 73 ERROR("%s:%d: *** mixed implicit and normal rules: deprecated syntax", 74 LOCF(loc)); 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 if ((term_index != string::npos && rest[term_index] == '=') || 88 (term_index == string::npos && term == '=')) { 89 if (term_index == string::npos) 90 term_index = rest.size(); 91 rule_var->outputs.swap(outputs); 92 ParseAssignStatement(rest, term_index, 93 &rule_var->lhs, &rule_var->rhs, &rule_var->op); 94 *out_rule = NULL; 95 return; 96 } 97 98 Rule* rule = new Rule(); 99 *out_rule = rule; 100 rule->loc = loc; 101 rule->is_double_colon = is_double_colon; 102 if (is_first_pattern) { 103 rule->output_patterns.swap(outputs); 104 } else { 105 rule->outputs.swap(outputs); 106 } 107 if (term_index != string::npos && term != ';') { 108 CHECK(rest[term_index] == ';'); 109 // TODO: Maybe better to avoid Intern here? 110 rule->cmds.push_back( 111 NewLiteral(Intern(TrimLeftSpace(rest.substr(term_index + 1))).str())); 112 rest = rest.substr(0, term_index); 113 } 114 115 index = rest.find(':'); 116 if (index == string::npos) { 117 ParseInputs(rule, rest); 118 return; 119 } 120 121 if (is_first_pattern) { 122 ERROR("%s:%d: *** mixed implicit and normal rules: deprecated syntax", 123 LOCF(loc)); 124 } 125 126 StringPiece second = rest.substr(0, index); 127 StringPiece third = rest.substr(index+1); 128 129 for (StringPiece tok : WordScanner(second)) { 130 tok = TrimLeadingCurdir(tok); 131 for (Symbol output : rule->outputs) { 132 if (!Pattern(tok).Match(output.str())) { 133 WARN("%s:%d: target `%s' doesn't match the target pattern", 134 LOCF(loc), output.c_str()); 135 } 136 } 137 138 rule->output_patterns.push_back(Intern(tok)); 139 } 140 141 if (rule->output_patterns.empty()) { 142 ERROR("%s:%d: *** missing target pattern.", LOCF(loc)); 143 } 144 if (rule->output_patterns.size() > 1) { 145 ERROR("%s:%d: *** multiple target patterns.", LOCF(loc)); 146 } 147 if (!IsPatternRule(rule->output_patterns[0].str())) { 148 ERROR("%s:%d: *** target pattern contains no '%%'.", LOCF(loc)); 149 } 150 ParseInputs(rule, third); 151 } 152 153 string Rule::DebugString() const { 154 vector<string> v; 155 v.push_back(StringPrintf("outputs=[%s]", JoinSymbols(outputs, ",").c_str())); 156 v.push_back(StringPrintf("inputs=[%s]", JoinSymbols(inputs, ",").c_str())); 157 if (!order_only_inputs.empty()) { 158 v.push_back(StringPrintf("order_only_inputs=[%s]", 159 JoinSymbols(order_only_inputs, ",").c_str())); 160 } 161 if (!output_patterns.empty()) { 162 v.push_back(StringPrintf("output_patterns=[%s]", 163 JoinSymbols(output_patterns, ",").c_str())); 164 } 165 if (is_double_colon) 166 v.push_back("is_double_colon"); 167 if (is_suffix_rule) 168 v.push_back("is_suffix_rule"); 169 if (!cmds.empty()) { 170 v.push_back(StringPrintf("cmds=[%s]", JoinValues(cmds, ",").c_str())); 171 } 172 return JoinStrings(v, " "); 173 } 174