blob: 1bea5056258b40ebe89682f23a7a64ac7c1ff376 [file] [log] [blame]
Shinichiro Hamajib69bf8a2015-06-10 14:52:06 +09001// 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
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090015package kati
Shinichiro Hamaji04932612015-03-31 23:46:56 +090016
17import (
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090018 "bytes"
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +090019 "errors"
Fumitoshi Ukaib97be672015-07-02 15:12:48 +090020 "fmt"
Shinichiro Hamaji04932612015-03-31 23:46:56 +090021 "strings"
22)
23
Fumitoshi Ukai935de962015-04-28 17:08:20 +090024type pattern struct {
25 prefix, suffix string
26}
27
28func (p pattern) String() string {
29 return p.prefix + "%" + p.suffix
30}
31
32func (p pattern) match(s string) bool {
33 return strings.HasPrefix(s, p.prefix) && strings.HasSuffix(s, p.suffix)
34}
35
36func (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
Fumitoshi Ukaiadc14442015-06-25 16:10:30 +090059type rule struct {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090060 srcpos
Fumitoshi Ukaib97be672015-07-02 15:12:48 +090061 // outputs is output of the rule.
62 // []string{} for ': xxx'
63 // nil for empty line.
64 outputs []string
65
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +090066 inputs []string
67 orderOnlyInputs []string
Fumitoshi Ukai935de962015-04-28 17:08:20 +090068 outputPatterns []pattern
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +090069 isDoubleColon bool
70 isSuffixRule bool
71 cmds []string
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +090072 cmdLineno int
Shinichiro Hamaji04932612015-03-31 23:46:56 +090073}
74
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090075func (r *rule) cmdpos() srcpos {
76 return srcpos{filename: r.filename, lineno: r.cmdLineno}
77}
78
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090079func isPatternRule(s []byte) (pattern, bool) {
Fumitoshi Ukai6addc2f2015-07-22 11:33:35 +090080 i := findLiteralChar(s, '%', 0, noSkipVar)
Fumitoshi Ukai935de962015-04-28 17:08:20 +090081 if i < 0 {
82 return pattern{}, false
83 }
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090084 return pattern{prefix: string(s[:i]), suffix: string(s[i+1:])}, true
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +090085}
86
Fumitoshi Ukai4b3a7dd2015-07-21 13:07:44 +090087func 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
Fumitoshi Ukai7c8b0ae2015-07-22 11:11:23 +0900102func 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
Fumitoshi Ukaiadc14442015-06-25 16:10:30 +0900113func (r *rule) parseInputs(s []byte) {
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +0900114 ws := newWordScanner(s)
Fumitoshi Ukai4b3a7dd2015-07-21 13:07:44 +0900115 ws.esc = true
Fumitoshi Ukai9c2f98c2015-07-22 16:46:08 +0900116 add := func(t string) {
117 r.inputs = append(r.inputs, t)
118 }
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +0900119 for ws.Scan() {
120 input := ws.Bytes()
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900121 if len(input) == 1 && input[0] == '|' {
Fumitoshi Ukai9c2f98c2015-07-22 16:46:08 +0900122 add = func(t string) {
123 r.orderOnlyInputs = append(r.orderOnlyInputs, t)
124 }
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +0900125 continue
126 }
Fumitoshi Ukai4b3a7dd2015-07-21 13:07:44 +0900127 input = unescapeInput(input)
Fumitoshi Ukai9c2f98c2015-07-22 16:46:08 +0900128 if !hasWildcardMetaByte(input) {
129 add(internBytes(input))
130 continue
131 }
Fumitoshi Ukai0547db62015-07-29 16:20:59 +0900132 m, _ := fsCache.Glob(string(input))
Fumitoshi Ukai9c2f98c2015-07-22 16:46:08 +0900133 if len(m) == 0 {
134 add(internBytes(input))
135 continue
136 }
137 for _, t := range m {
138 add(intern(t))
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +0900139 }
140 }
141}
142
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900143func (r *rule) parseVar(s []byte, rhs expr) (*assignAST, error) {
144 var lhsBytes []byte
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900145 var op string
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900146 // TODO(ukai): support override, export.
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900147 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 '='
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900151 case ':':
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900152 lhsBytes = trimSpaceBytes(s[:len(s)-2])
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900153 op = ":="
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900154 case '+':
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900155 lhsBytes = trimSpaceBytes(s[:len(s)-2])
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900156 op = "+="
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900157 case '?':
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900158 lhsBytes = trimSpaceBytes(s[:len(s)-2])
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900159 op = "?="
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900160 default:
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900161 lhsBytes = trimSpaceBytes(s[:len(s)-1])
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900162 op = "="
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900163 }
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900164 assign := &assignAST{
Fumitoshi Ukai91608db2015-07-07 11:52:56 +0900165 lhs: literal(string(lhsBytes)),
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900166 rhs: compactExpr(rhs),
167 op: op,
168 }
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900169 assign.srcpos = r.srcpos
170 return assign, nil
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900171}
172
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900173// parse parses rule line.
Fumitoshi Ukai6addc2f2015-07-22 11:33:35 +0900174// 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 '$'.
Fumitoshi Ukai201df422015-07-07 17:31:05 +0900177// 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)
179func (r *rule) parse(line []byte, assign *assignAST, rhs expr) (*assignAST, error) {
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900180 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
Fumitoshi Ukai6addc2f2015-07-22 11:33:35 +0900187 index := findLiteralChar(line, ':', 0, noSkipVar)
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900188 if index < 0 {
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900189 return nil, errors.New("*** missing separator.")
Shinichiro Hamaji04932612015-03-31 23:46:56 +0900190 }
191
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900192 first := line[:index]
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +0900193 ws := newWordScanner(first)
Fumitoshi Ukai7c8b0ae2015-07-22 11:11:23 +0900194 ws.esc = true
Fumitoshi Ukai935de962015-04-28 17:08:20 +0900195 pat, isFirstPattern := isPatternRule(first)
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900196 if isFirstPattern {
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +0900197 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 }
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900203 }
Fumitoshi Ukai935de962015-04-28 17:08:20 +0900204 r.outputPatterns = []pattern{pat}
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900205 } else {
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +0900206 for ws.Scan() {
Fumitoshi Ukai9c2f98c2015-07-22 16:46:08 +0900207 // TODO(ukai): expand raw wildcard for output. any usage?
Fumitoshi Ukai7c8b0ae2015-07-22 11:11:23 +0900208 r.outputs = append(r.outputs, internBytes(unescapeTarget(ws.Bytes())))
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900209 }
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900210 }
211
212 index++
213 if index < len(line) && line[index] == ':' {
214 r.isDoubleColon = true
215 index++
216 }
217
218 rest := line[index:]
Fumitoshi Ukai201df422015-07-07 17:31:05 +0900219 if assign != nil {
220 if len(rest) > 0 {
221 panic(fmt.Sprintf("pattern specific var? line:%q", line))
222 }
223 return assign, nil
224 }
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900225 if rhs != nil {
226 assign, err := r.parseVar(rest, rhs)
227 if err != nil {
228 return nil, err
229 }
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900230 return assign, nil
231 }
Fumitoshi Ukai8f4be412015-07-07 11:57:13 +0900232 index = bytes.IndexByte(rest, ';')
233 if index >= 0 {
234 r.cmds = append(r.cmds, string(rest[index+1:]))
235 rest = rest[:index-1]
236 }
Fumitoshi Ukai6addc2f2015-07-22 11:33:35 +0900237 index = findLiteralChar(rest, ':', 0, noSkipVar)
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900238 if index < 0 {
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +0900239 r.parseInputs(rest)
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900240 return nil, nil
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900241 }
242
243 // %.x: %.y: %.z
244 if isFirstPattern {
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900245 return nil, errors.New("*** mixed implicit and normal rules: deprecated syntax")
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900246 }
247
248 second := rest[:index]
249 third := rest[index+1:]
250
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900251 // r.outputs is already set.
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +0900252 ws = newWordScanner(second)
253 if !ws.Scan() {
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900254 return nil, errors.New("*** missing target pattern.")
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900255 }
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +0900256 outpat, ok := isPatternRule(ws.Bytes())
Fumitoshi Ukai935de962015-04-28 17:08:20 +0900257 if !ok {
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900258 return nil, errors.New("*** target pattern contains no '%'.")
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900259 }
Fumitoshi Ukai935de962015-04-28 17:08:20 +0900260 r.outputPatterns = []pattern{outpat}
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +0900261 if ws.Scan() {
262 return nil, errors.New("*** multiple target patterns.")
263 }
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +0900264 r.parseInputs(third)
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900265
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900266 return nil, nil
Shinichiro Hamaji04932612015-03-31 23:46:56 +0900267}