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 package kati
     16 
     17 import (
     18 	"bytes"
     19 	"errors"
     20 	"fmt"
     21 	"strings"
     22 )
     23 
     24 type pattern struct {
     25 	prefix, suffix string
     26 }
     27 
     28 func (p pattern) String() string {
     29 	return p.prefix + "%" + p.suffix
     30 }
     31 
     32 func (p pattern) match(s string) bool {
     33 	return strings.HasPrefix(s, p.prefix) && strings.HasSuffix(s, p.suffix)
     34 }
     35 
     36 func (p pattern) subst(repl, str string) string {
     37 	in := str
     38 	trimed := str
     39 	if p.prefix != "" {
     40 		trimed = strings.TrimPrefix(in, p.prefix)
     41 		if trimed == in {
     42 			return str
     43 		}
     44 	}
     45 	in = trimed
     46 	if p.suffix != "" {
     47 		trimed = strings.TrimSuffix(in, p.suffix)
     48 		if trimed == in {
     49 			return str
     50 		}
     51 	}
     52 	rs := strings.SplitN(repl, "%", 2)
     53 	if len(rs) != 2 {
     54 		return repl
     55 	}
     56 	return rs[0] + trimed + rs[1]
     57 }
     58 
     59 type rule struct {
     60 	srcpos
     61 	// outputs is output of the rule.
     62 	// []string{} for ': xxx'
     63 	// nil for empty line.
     64 	outputs []string
     65 
     66 	inputs          []string
     67 	orderOnlyInputs []string
     68 	outputPatterns  []pattern
     69 	isDoubleColon   bool
     70 	isSuffixRule    bool
     71 	cmds            []string
     72 	cmdLineno       int
     73 }
     74 
     75 func (r *rule) cmdpos() srcpos {
     76 	return srcpos{filename: r.filename, lineno: r.cmdLineno}
     77 }
     78 
     79 func isPatternRule(s []byte) (pattern, bool) {
     80 	i := findLiteralChar(s, '%', 0, noSkipVar)
     81 	if i < 0 {
     82 		return pattern{}, false
     83 	}
     84 	return pattern{prefix: string(s[:i]), suffix: string(s[i+1:])}, true
     85 }
     86 
     87 func unescapeInput(s []byte) []byte {
     88 	// only "\ ", "\=" becoms " ", "=" respectively?
     89 	// other \-escape, such as "\:" keeps "\:".
     90 	for i := 0; i < len(s); i++ {
     91 		if s[i] != '\\' {
     92 			continue
     93 		}
     94 		if i+1 < len(s) && s[i+1] == ' ' || s[i+1] == '=' {
     95 			copy(s[i:], s[i+1:])
     96 			s = s[:len(s)-1]
     97 		}
     98 	}
     99 	return s
    100 }
    101 
    102 func unescapeTarget(s []byte) []byte {
    103 	for i := 0; i < len(s); i++ {
    104 		if s[i] != '\\' {
    105 			continue
    106 		}
    107 		copy(s[i:], s[i+1:])
    108 		s = s[:len(s)-1]
    109 	}
    110 	return s
    111 }
    112 
    113 func (r *rule) parseInputs(s []byte) {
    114 	ws := newWordScanner(s)
    115 	ws.esc = true
    116 	add := func(t string) {
    117 		r.inputs = append(r.inputs, t)
    118 	}
    119 	for ws.Scan() {
    120 		input := ws.Bytes()
    121 		if len(input) == 1 && input[0] == '|' {
    122 			add = func(t string) {
    123 				r.orderOnlyInputs = append(r.orderOnlyInputs, t)
    124 			}
    125 			continue
    126 		}
    127 		input = unescapeInput(input)
    128 		if !hasWildcardMetaByte(input) {
    129 			add(internBytes(input))
    130 			continue
    131 		}
    132 		m, _ := fsCache.Glob(string(input))
    133 		if len(m) == 0 {
    134 			add(internBytes(input))
    135 			continue
    136 		}
    137 		for _, t := range m {
    138 			add(intern(t))
    139 		}
    140 	}
    141 }
    142 
    143 func (r *rule) parseVar(s []byte, rhs expr) (*assignAST, error) {
    144 	var lhsBytes []byte
    145 	var op string
    146 	// TODO(ukai): support override, export.
    147 	if s[len(s)-1] != '=' {
    148 		panic(fmt.Sprintf("unexpected lhs %q", s))
    149 	}
    150 	switch s[len(s)-2] { // s[len(s)-1] is '='
    151 	case ':':
    152 		lhsBytes = trimSpaceBytes(s[:len(s)-2])
    153 		op = ":="
    154 	case '+':
    155 		lhsBytes = trimSpaceBytes(s[:len(s)-2])
    156 		op = "+="
    157 	case '?':
    158 		lhsBytes = trimSpaceBytes(s[:len(s)-2])
    159 		op = "?="
    160 	default:
    161 		lhsBytes = trimSpaceBytes(s[:len(s)-1])
    162 		op = "="
    163 	}
    164 	assign := &assignAST{
    165 		lhs: literal(string(lhsBytes)),
    166 		rhs: compactExpr(rhs),
    167 		op:  op,
    168 	}
    169 	assign.srcpos = r.srcpos
    170 	return assign, nil
    171 }
    172 
    173 // parse parses rule line.
    174 // line is rule line until '=', or before ';'.
    175 // line was already expaned, so probably no need to skip var $(xxx) when
    176 // finding literal char. i.e. $ is parsed as literal '$'.
    177 // assign is not nil, if line was known as target specific var '<xxx>: <v>=<val>'
    178 // rhs is not nil, if line ended with '=' (target specific var after evaluated)
    179 func (r *rule) parse(line []byte, assign *assignAST, rhs expr) (*assignAST, error) {
    180 	line = trimLeftSpaceBytes(line)
    181 	// See semicolon.mk.
    182 	if rhs == nil && (len(line) == 0 || line[0] == ';') {
    183 		return nil, nil
    184 	}
    185 	r.outputs = []string{}
    186 
    187 	index := findLiteralChar(line, ':', 0, noSkipVar)
    188 	if index < 0 {
    189 		return nil, errors.New("*** missing separator.")
    190 	}
    191 
    192 	first := line[:index]
    193 	ws := newWordScanner(first)
    194 	ws.esc = true
    195 	pat, isFirstPattern := isPatternRule(first)
    196 	if isFirstPattern {
    197 		n := 0
    198 		for ws.Scan() {
    199 			n++
    200 			if n > 1 {
    201 				return nil, errors.New("*** mixed implicit and normal rules: deprecated syntax")
    202 			}
    203 		}
    204 		r.outputPatterns = []pattern{pat}
    205 	} else {
    206 		for ws.Scan() {
    207 			// TODO(ukai): expand raw wildcard for output. any usage?
    208 			r.outputs = append(r.outputs, internBytes(unescapeTarget(ws.Bytes())))
    209 		}
    210 	}
    211 
    212 	index++
    213 	if index < len(line) && line[index] == ':' {
    214 		r.isDoubleColon = true
    215 		index++
    216 	}
    217 
    218 	rest := line[index:]
    219 	if assign != nil {
    220 		if len(rest) > 0 {
    221 			panic(fmt.Sprintf("pattern specific var? line:%q", line))
    222 		}
    223 		return assign, nil
    224 	}
    225 	if rhs != nil {
    226 		assign, err := r.parseVar(rest, rhs)
    227 		if err != nil {
    228 			return nil, err
    229 		}
    230 		return assign, nil
    231 	}
    232 	index = bytes.IndexByte(rest, ';')
    233 	if index >= 0 {
    234 		r.cmds = append(r.cmds, string(rest[index+1:]))
    235 		rest = rest[:index-1]
    236 	}
    237 	index = findLiteralChar(rest, ':', 0, noSkipVar)
    238 	if index < 0 {
    239 		r.parseInputs(rest)
    240 		return nil, nil
    241 	}
    242 
    243 	// %.x: %.y: %.z
    244 	if isFirstPattern {
    245 		return nil, errors.New("*** mixed implicit and normal rules: deprecated syntax")
    246 	}
    247 
    248 	second := rest[:index]
    249 	third := rest[index+1:]
    250 
    251 	// r.outputs is already set.
    252 	ws = newWordScanner(second)
    253 	if !ws.Scan() {
    254 		return nil, errors.New("*** missing target pattern.")
    255 	}
    256 	outpat, ok := isPatternRule(ws.Bytes())
    257 	if !ok {
    258 		return nil, errors.New("*** target pattern contains no '%'.")
    259 	}
    260 	r.outputPatterns = []pattern{outpat}
    261 	if ws.Scan() {
    262 		return nil, errors.New("*** multiple target patterns.")
    263 	}
    264 	r.parseInputs(third)
    265 
    266 	return nil, nil
    267 }
    268