Home | History | Annotate | Download | only in kati
      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