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
ParseInputs(Rule * r,StringPiece s)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
IsPatternRule(StringPiece s)44 bool IsPatternRule(StringPiece s) {
45 return s.find('%') != string::npos;
46 }
47
48 } // namespace
49
Rule()50 Rule::Rule()
51 : is_double_colon(false),
52 is_suffix_rule(false),
53 cmd_lineno(0) {
54 }
55
ParseRule(Loc & loc,StringPiece line,char term,Rule ** out_rule,RuleVarAssignment * rule_var)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
DebugString() const153 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