blob: 527d7c46211a116c3ac0d8d38725ba2e1579a1b7 [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
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090016
Fumitoshi Ukai0aa4fc42015-04-10 17:00:19 +090017//go:generate go run testcase/gen_testcase_parse_benchmark.go
18//
19// $ go generate
20// $ go test -bench .
21
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090022import (
23 "bufio"
Shinichiro Hamajie1841582015-03-30 17:20:33 +090024 "bytes"
Fumitoshi Ukai9042b992015-06-23 16:10:27 +090025 "crypto/sha1"
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +090026 "fmt"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090027 "io"
Fumitoshi Ukai9042b992015-06-23 16:10:27 +090028 "io/ioutil"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090029 "os"
Shinichiro Hamajid7bef602015-03-30 19:55:32 +090030 "strings"
Fumitoshi Ukai9042b992015-06-23 16:10:27 +090031 "sync"
Shinichiro Hamaji584bb062015-06-04 13:25:13 +090032 "time"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090033)
34
35type Makefile struct {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +090036 filename string
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +090037 stmts []ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090038}
39
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090040type ifState struct {
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +090041 ast *ifAST
Shinichiro Hamajia06760f2015-04-07 13:13:45 +090042 inElse bool
43 numNest int
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090044}
45
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090046type parser struct {
Shinichiro Hamaji370be722015-04-10 14:55:23 +090047 rd *bufio.Reader
48 mk Makefile
49 lineno int
50 elineno int // lineno == elineno unless there is trailing '\'.
51 linenoFixed bool
52 unBuf []byte
53 hasUnBuf bool
54 done bool
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +090055 outStmts *[]ast
Shinichiro Hamaji370be722015-04-10 14:55:23 +090056 ifStack []ifState
57 inDef []string
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +090058 defOpt string
Shinichiro Hamaji370be722015-04-10 14:55:23 +090059 numIfNest int
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090060}
61
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090062func newParser(rd io.Reader, filename string) *parser {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090063 p := &parser{
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +090064 rd: bufio.NewReader(rd),
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090065 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +090066 p.mk.filename = filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090067 p.outStmts = &p.mk.stmts
68 return p
69}
70
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +090071func (p *parser) addStatement(stmt ast) {
72 *p.outStmts = append(*p.outStmts, stmt)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090073}
74
Shinichiro Hamajie1841582015-03-30 17:20:33 +090075func (p *parser) readLine() []byte {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090076 if p.hasUnBuf {
77 p.hasUnBuf = false
78 return p.unBuf
Shinichiro Hamajie1841582015-03-30 17:20:33 +090079 }
80
Shinichiro Hamaji370be722015-04-10 14:55:23 +090081 if !p.linenoFixed {
82 p.lineno = p.elineno
83 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +090084 line, err := p.rd.ReadBytes('\n')
Shinichiro Hamaji370be722015-04-10 14:55:23 +090085 if !p.linenoFixed {
86 p.lineno++
87 p.elineno = p.lineno
88 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +090089 if err == io.EOF {
90 p.done = true
91 } else if err != nil {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +090092 panic(fmt.Errorf("readline %s:%d: %v", p.mk.filename, p.lineno, err))
Shinichiro Hamajie1841582015-03-30 17:20:33 +090093 }
94
Shinichiro Hamaji56c868c2015-04-09 10:17:10 +090095 line = bytes.TrimRight(line, "\r\n")
Shinichiro Hamajie1841582015-03-30 17:20:33 +090096
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +090097 return line
98}
99
100func removeComment(line []byte) []byte {
101 var parenStack []byte
Shinichiro Hamajif4d3ee52015-04-22 16:19:33 +0900102 // Do not use range as we may modify |line| and |i|.
103 for i := 0; i < len(line); i++ {
104 ch := line[i]
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900105 switch ch {
106 case '(', '{':
107 parenStack = append(parenStack, ch)
108 case ')', '}':
109 if len(parenStack) > 0 {
Shinichiro Hamajif863d862015-06-17 16:48:37 +0900110 cp := closeParen(parenStack[len(parenStack)-1])
111 if cp == ch {
112 parenStack = parenStack[:len(parenStack)-1]
113 }
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900114 }
115 case '#':
116 if len(parenStack) == 0 {
Shinichiro Hamajif4d3ee52015-04-22 16:19:33 +0900117 if i == 0 || line[i-1] != '\\' {
118 return line[:i]
119 }
120 // Drop the backslash before '#'.
121 line = append(line[:i-1], line[i:]...)
122 i--
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900123 }
124 }
125 }
126 return line
127}
128
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900129func hasTrailingBackslash(line []byte) bool {
130 if len(line) == 0 {
131 return false
132 }
133 if line[len(line)-1] != '\\' {
134 return false
135 }
136 return len(line) <= 1 || line[len(line)-2] != '\\'
137}
138
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900139func (p *parser) processDefineLine(line []byte) []byte {
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900140 for hasTrailingBackslash(line) {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900141 line = line[:len(line)-1]
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900142 line = bytes.TrimRight(line, "\t ")
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900143 lineno := p.lineno
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900144 nline := trimLeftSpaceBytes(p.readLine())
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900145 p.lineno = lineno
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900146 line = append(line, ' ')
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900147 line = append(line, nline...)
148 }
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900149 return line
150}
151
152func (p *parser) processMakefileLine(line []byte) []byte {
153 return removeComment(p.processDefineLine(line))
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900154}
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900155
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900156func (p *parser) processRecipeLine(line []byte) []byte {
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900157 for hasTrailingBackslash(line) {
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900158 line = append(line, '\n')
159 lineno := p.lineno
160 nline := p.readLine()
161 p.lineno = lineno
162 line = append(line, nline...)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900163 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900164 return line
165}
166
167func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900168 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900169 panic("unreadLine twice!")
170 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900171 p.unBuf = line
172 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900173}
174
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900175func newAssignAST(p *parser, lhsBytes []byte, rhsBytes []byte, op string) *assignAST {
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900176 lhs, _, err := parseExpr(lhsBytes, nil, true)
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900177 if err != nil {
178 panic(err)
179 }
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900180 rhs, _, err := parseExpr(rhsBytes, nil, true)
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900181 if err != nil {
182 panic(err)
183 }
184 opt := ""
185 if p != nil {
186 opt = p.defOpt
187 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900188 return &assignAST{
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900189 lhs: lhs,
190 rhs: rhs,
191 op: op,
192 opt: opt,
193 }
194}
195
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900196func (p *parser) parseAssign(line []byte, sep, esep int) ast {
Fumitoshi Ukai8fabdd02015-06-08 10:27:47 +0900197 Logf("parseAssign %q op:%q", line, line[sep:esep])
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900198 aast := newAssignAST(p, bytes.TrimSpace(line[:sep]), trimLeftSpaceBytes(line[esep:]), string(line[sep:esep]))
199 aast.filename = p.mk.filename
200 aast.lineno = p.lineno
201 return aast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900202}
203
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900204func (p *parser) parseMaybeRule(line []byte, equalIndex, semicolonIndex int) ast {
Shinichiro Hamaji50309a62015-06-04 14:01:55 +0900205 if len(trimSpaceBytes(line)) == 0 {
Shinichiro Hamajide829712015-03-31 18:26:56 +0900206 return nil
207 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900208
Shinichiro Hamaji171a3df2015-06-04 14:17:45 +0900209 expr := line
210 var term byte
211 var afterTerm []byte
212
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900213 // Either '=' or ';' is used.
214 if equalIndex >= 0 && semicolonIndex >= 0 {
215 if equalIndex < semicolonIndex {
216 semicolonIndex = -1
217 } else {
218 equalIndex = -1
219 }
220 }
Shinichiro Hamaji171a3df2015-06-04 14:17:45 +0900221 if semicolonIndex >= 0 {
222 afterTerm = expr[semicolonIndex:]
223 expr = expr[0:semicolonIndex]
224 term = ';'
225 } else if equalIndex >= 0 {
226 afterTerm = expr[equalIndex:]
227 expr = expr[0:equalIndex]
228 term = '='
229 }
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900230
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900231 v, _, err := parseExpr(expr, nil, true)
Shinichiro Hamaji2d4b6052015-06-04 14:20:44 +0900232 if err != nil {
233 panic(fmt.Errorf("parse %s:%d %v", p.mk.filename, p.lineno, err))
234 }
235
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900236 rast := &maybeRuleAST{
Shinichiro Hamaji2d4b6052015-06-04 14:20:44 +0900237 expr: v,
Shinichiro Hamaji171a3df2015-06-04 14:17:45 +0900238 term: term,
239 afterTerm: afterTerm,
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900240 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900241 rast.filename = p.mk.filename
242 rast.lineno = p.lineno
243 return rast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900244}
245
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900246func (p *parser) parseInclude(line string, oplen int) ast {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900247 // TODO(ukai): parse expr here
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900248 iast := &includeAST{
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900249 expr: line[oplen+1:],
250 op: line[:oplen],
251 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900252 iast.filename = p.mk.filename
253 iast.lineno = p.lineno
254 return iast
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900255}
256
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900257func (p *parser) parseIfdef(line []byte, oplen int) ast {
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900258 lhs, _, err := parseExpr(trimLeftSpaceBytes(line[oplen+1:]), nil, true)
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900259 if err != nil {
260 panic(fmt.Errorf("ifdef parse %s:%d %v", p.mk.filename, p.lineno, err))
261 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900262 iast := &ifAST{
Shinichiro Hamaji61d2e112015-06-04 14:56:47 +0900263 op: string(line[:oplen]),
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900264 lhs: lhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900265 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900266 iast.filename = p.mk.filename
267 iast.lineno = p.lineno
268 p.addStatement(iast)
269 p.ifStack = append(p.ifStack, ifState{ast: iast, numNest: p.numIfNest})
270 p.outStmts = &iast.trueStmts
271 return iast
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900272}
273
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900274func (p *parser) parseTwoQuotes(s string, op string) ([]string, bool) {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900275 var args []string
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900276 for i := 0; i < 2; i++ {
277 s = strings.TrimSpace(s)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900278 if s == "" {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900279 return nil, false
280 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900281 quote := s[0]
282 if quote != '\'' && quote != '"' {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900283 return nil, false
284 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900285 end := strings.IndexByte(s[1:], quote) + 1
286 if end < 0 {
287 return nil, false
288 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900289 args = append(args, s[1:end])
290 s = s[end+1:]
291 }
292 if len(s) > 0 {
293 Error(p.mk.filename, p.lineno, `extraneous text after %q directive`, op)
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900294 }
295 return args, true
296}
297
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900298// parse
299// "(lhs, rhs)"
300// "lhs, rhs"
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900301func (p *parser) parseEq(s string, op string) (string, string, bool) {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900302 if s[0] == '(' && s[len(s)-1] == ')' {
303 s = s[1 : len(s)-1]
304 term := []byte{','}
305 in := []byte(s)
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900306 v, n, err := parseExpr(in, term, false)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900307 if err != nil {
308 return "", "", false
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900309 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900310 lhs := v.String()
311 n++
312 n += skipSpaces(in[n:], nil)
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900313 v, n, err = parseExpr(in[n:], nil, false)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900314 if err != nil {
315 return "", "", false
316 }
317 rhs := v.String()
318 return lhs, rhs, true
319 }
320 args, ok := p.parseTwoQuotes(s, op)
321 if !ok {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900322 return "", "", false
323 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900324 return args[0], args[1], true
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900325}
326
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900327func (p *parser) parseIfeq(line string, oplen int) ast {
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900328 op := line[:oplen]
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900329 lhsBytes, rhsBytes, ok := p.parseEq(strings.TrimSpace(line[oplen+1:]), op)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900330 if !ok {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900331 Error(p.mk.filename, p.lineno, `*** invalid syntax in conditional.`)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900332 }
333
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900334 lhs, _, err := parseExpr([]byte(lhsBytes), nil, true)
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900335 if err != nil {
336 panic(fmt.Errorf("parse ifeq lhs %s:%d %v", p.mk.filename, p.lineno, err))
337 }
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900338 rhs, _, err := parseExpr([]byte(rhsBytes), nil, true)
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900339 if err != nil {
340 panic(fmt.Errorf("parse ifeq rhs %s:%d %v", p.mk.filename, p.lineno, err))
341 }
342
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900343 iast := &ifAST{
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900344 op: op,
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900345 lhs: lhs,
346 rhs: rhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900347 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900348 iast.filename = p.mk.filename
349 iast.lineno = p.lineno
350 p.addStatement(iast)
351 p.ifStack = append(p.ifStack, ifState{ast: iast, numNest: p.numIfNest})
352 p.outStmts = &iast.trueStmts
353 return iast
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900354}
355
356func (p *parser) checkIfStack(curKeyword string) {
357 if len(p.ifStack) == 0 {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900358 Error(p.mk.filename, p.lineno, `*** extraneous %q.`, curKeyword)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900359 }
360}
361
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900362func (p *parser) parseElse(line []byte) {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900363 p.checkIfStack("else")
364 state := &p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900365 if state.inElse {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900366 Error(p.mk.filename, p.lineno, `*** only one "else" per conditional.`)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900367 }
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900368 state.inElse = true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900369 p.outStmts = &state.ast.falseStmts
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900370
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900371 nextIf := trimLeftSpaceBytes(line[len("else"):])
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900372 if len(nextIf) == 0 {
373 return
374 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900375 var ifDirectives = map[string]directiveFunc{
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900376 "ifdef ": ifdefDirective,
377 "ifndef ": ifndefDirective,
378 "ifeq ": ifeqDirective,
379 "ifneq ": ifneqDirective,
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900380 }
381 p.numIfNest = state.numNest + 1
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900382 if f, ok := p.isDirective(nextIf, ifDirectives); ok {
383 f(p, nextIf)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900384 p.numIfNest = 0
385 return
386 }
387 p.numIfNest = 0
388 WarnNoPrefix(p.mk.filename, p.lineno, "extraneous text after `else` directive")
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900389}
390
391func (p *parser) parseEndif(line string) {
392 p.checkIfStack("endif")
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900393 state := p.ifStack[len(p.ifStack)-1]
394 for t := 0; t <= state.numNest; t++ {
395 p.ifStack = p.ifStack[0 : len(p.ifStack)-1]
396 if len(p.ifStack) == 0 {
397 p.outStmts = &p.mk.stmts
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900398 } else {
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900399 state := p.ifStack[len(p.ifStack)-1]
400 if state.inElse {
401 p.outStmts = &state.ast.falseStmts
402 } else {
403 p.outStmts = &state.ast.trueStmts
404 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900405 }
406 }
407}
408
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900409type directiveFunc func(*parser, []byte) []byte
410
411var makeDirectives = map[string]directiveFunc{
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900412 "include ": includeDirective,
413 "-include ": sincludeDirective,
414 "sinclude": sincludeDirective,
415 "ifdef ": ifdefDirective,
416 "ifndef ": ifndefDirective,
417 "ifeq ": ifeqDirective,
418 "ifneq ": ifneqDirective,
419 "else": elseDirective,
420 "endif": endifDirective,
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900421 "define ": defineDirective,
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900422 "override ": overrideDirective,
423 "export ": exportDirective,
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900424 "unexport ": unexportDirective,
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900425}
426
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900427// TODO(ukai): use []byte
428func (p *parser) isDirective(line []byte, directives map[string]directiveFunc) (directiveFunc, bool) {
429 stripped := trimLeftSpaceBytes(line)
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900430 // Fast paths.
431 // TODO: Consider using a trie.
432 if len(stripped) == 0 {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900433 return nil, false
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900434 }
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900435 if ch := stripped[0]; ch != 'i' && ch != '-' && ch != 's' && ch != 'e' && ch != 'd' && ch != 'o' && ch != 'u' {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900436 return nil, false
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900437 }
438
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900439 for prefix, f := range directives {
440 if bytes.HasPrefix(stripped, []byte(prefix)) {
441 return f, true
442 }
443 if prefix[len(prefix)-1] == ' ' && bytes.HasPrefix(stripped, []byte(prefix[:len(prefix)-1])) && stripped[len(prefix)-1] == '\t' {
444 return f, true
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900445 }
446 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900447 return nil, false
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900448}
449
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900450func includeDirective(p *parser, line []byte) []byte {
451 p.addStatement(p.parseInclude(string(line), len("include")))
452 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900453}
454
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900455func sincludeDirective(p *parser, line []byte) []byte {
456 p.addStatement(p.parseInclude(string(line), len("-include")))
457 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900458}
459
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900460func ifdefDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji61d2e112015-06-04 14:56:47 +0900461 p.parseIfdef(line, len("ifdef"))
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900462 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900463}
464
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900465func ifndefDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji61d2e112015-06-04 14:56:47 +0900466 p.parseIfdef(line, len("ifndef"))
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900467 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900468}
469
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900470func ifeqDirective(p *parser, line []byte) []byte {
471 p.parseIfeq(string(line), len("ifeq"))
472 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900473}
474
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900475func ifneqDirective(p *parser, line []byte) []byte {
476 p.parseIfeq(string(line), len("ifneq"))
477 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900478}
479
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900480func elseDirective(p *parser, line []byte) []byte {
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900481 p.parseElse(line)
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900482 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900483}
484
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900485func endifDirective(p *parser, line []byte) []byte {
486 p.parseEndif(string(line))
487 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900488}
489
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900490func defineDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji72ae2dd2015-06-17 15:47:15 +0900491 lhs := trimLeftSpaceBytes(line[len("define "):])
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900492 p.inDef = []string{string(lhs)}
493 return nil
494}
495
496func overrideDirective(p *parser, line []byte) []byte {
497 p.defOpt = "override"
498 line = trimLeftSpaceBytes(line[len("override "):])
499 defineDirective := map[string]directiveFunc{
500 "define": defineDirective,
501 }
502 if f, ok := p.isDirective(line, defineDirective); ok {
503 f(p, line)
504 return nil
505 }
506 // e.g. overrider foo := bar
507 // line will be "foo := bar".
508 return line
509}
510
Shinichiro Hamajiea553f32015-05-29 17:03:33 +0900511func handleExport(p *parser, line []byte, export bool) (hasEqual bool) {
512 equalIndex := bytes.IndexByte(line, '=')
513 if equalIndex > 0 {
514 hasEqual = true
515 switch line[equalIndex-1] {
516 case ':', '+', '?':
517 equalIndex--
518 }
519 line = line[:equalIndex]
520 }
521
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900522 east := &exportAST{
Shinichiro Hamajif61033d2015-05-29 15:01:48 +0900523 expr: line,
524 export: export,
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900525 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900526 east.filename = p.mk.filename
527 east.lineno = p.lineno
528 p.addStatement(east)
Shinichiro Hamajiea553f32015-05-29 17:03:33 +0900529 return hasEqual
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900530}
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900531
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900532func exportDirective(p *parser, line []byte) []byte {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900533 p.defOpt = "export"
534 line = trimLeftSpaceBytes(line[len("export "):])
535 defineDirective := map[string]directiveFunc{
536 "define": defineDirective,
537 }
538 if f, ok := p.isDirective(line, defineDirective); ok {
539 f(p, line)
540 return nil
541 }
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900542
Shinichiro Hamajiea553f32015-05-29 17:03:33 +0900543 if !handleExport(p, line, true) {
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900544 return nil
545 }
546
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900547 // e.g. export foo := bar
548 // line will be "foo := bar".
549 return line
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900550}
551
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900552func unexportDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900553 handleExport(p, line[len("unexport "):], false)
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900554 return nil
555}
556
Shinichiro Hamajica668572015-06-17 06:36:45 +0900557func (p *parser) isEndef(s string) bool {
558 if s == "endef" {
559 return true
560 }
561 found := strings.IndexAny(s, " \t")
562 if found >= 0 && s[:found] == "endef" {
563 rest := strings.TrimSpace(s[found+1:])
564 if rest != "" && rest[0] != '#' {
565 WarnNoPrefix(p.mk.filename, p.lineno, "extraneous text after \"endef\" directive")
566 }
567 return true
568 }
569 return false
570}
571
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900572func (p *parser) parse() (mk Makefile, err error) {
573 defer func() {
574 if r := recover(); r != nil {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +0900575 err = fmt.Errorf("panic in parse %s: %v", mk.filename, r)
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900576 }
577 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900578 for !p.done {
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900579 line := p.readLine()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900580
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900581 if len(p.inDef) > 0 {
Shinichiro Hamajica668572015-06-17 06:36:45 +0900582 lineStr := string(p.processDefineLine(line))
583 if p.isEndef(lineStr) {
Fumitoshi Ukai8fabdd02015-06-08 10:27:47 +0900584 Logf("multilineAssign %q", p.inDef)
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900585 aast := newAssignAST(p, []byte(p.inDef[0]), []byte(strings.Join(p.inDef[1:], "\n")), "=")
586 aast.filename = p.mk.filename
587 aast.lineno = p.lineno - len(p.inDef)
588 p.addStatement(aast)
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900589 p.inDef = nil
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900590 p.defOpt = ""
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900591 continue
592 }
Shinichiro Hamajica668572015-06-17 06:36:45 +0900593 p.inDef = append(p.inDef, lineStr)
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900594 continue
595 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900596 p.defOpt = ""
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900597
Shinichiro Hamaji960161f2015-04-13 17:10:10 +0900598 if len(bytes.TrimSpace(line)) == 0 {
599 continue
600 }
601
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900602 if f, ok := p.isDirective(line, makeDirectives); ok {
Shinichiro Hamaji72ae2dd2015-06-17 15:47:15 +0900603 line = trimSpaceBytes(p.processMakefileLine(line))
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900604 line = f(p, line)
605 if len(line) == 0 {
606 continue
607 }
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900608 }
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900609 if line[0] == '\t' {
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900610 cast := &commandAST{cmd: string(p.processRecipeLine(line[1:]))}
611 cast.filename = p.mk.filename
612 cast.lineno = p.lineno
613 p.addStatement(cast)
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900614 continue
615 }
616
617 line = p.processMakefileLine(line)
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900618
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900619 var stmt ast
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900620 var parenStack []byte
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900621 equalIndex := -1
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900622 semicolonIndex := -1
623 isRule := false
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900624 for i, ch := range line {
625 switch ch {
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900626 case '(', '{':
627 parenStack = append(parenStack, ch)
628 case ')', '}':
629 if len(parenStack) == 0 {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900630 Warn(p.mk.filename, p.lineno, "Unmatched parens: %s", line)
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900631 } else {
Shinichiro Hamajif863d862015-06-17 16:48:37 +0900632 cp := closeParen(parenStack[len(parenStack)-1])
633 if cp == ch {
634 parenStack = parenStack[:len(parenStack)-1]
635 }
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900636 }
637 }
638 if len(parenStack) > 0 {
639 continue
640 }
641
642 switch ch {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900643 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900644 if i+1 < len(line) && line[i+1] == '=' {
Shinichiro Hamaji156ef3e2015-04-11 22:44:41 +0900645 if !isRule {
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900646 stmt = p.parseAssign(line, i, i+2)
Shinichiro Hamaji156ef3e2015-04-11 22:44:41 +0900647 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900648 } else {
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900649 isRule = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900650 }
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900651 case ';':
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900652 if semicolonIndex < 0 {
653 semicolonIndex = i
654 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900655 case '=':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900656 if !isRule {
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900657 stmt = p.parseAssign(line, i, i+1)
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900658 }
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900659 if equalIndex < 0 {
660 equalIndex = i
661 }
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900662 case '?', '+':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900663 if !isRule && i+1 < len(line) && line[i+1] == '=' {
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900664 stmt = p.parseAssign(line, i, i+2)
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900665 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900666 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900667 if stmt != nil {
668 p.addStatement(stmt)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900669 break
670 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900671 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900672 if stmt == nil {
673 stmt = p.parseMaybeRule(line, equalIndex, semicolonIndex)
674 if stmt != nil {
675 p.addStatement(stmt)
Shinichiro Hamajide829712015-03-31 18:26:56 +0900676 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900677 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900678 }
679 return p.mk, nil
680}
681
Shinichiro Hamaji290eb252015-05-19 18:03:19 +0900682func ParseMakefileFd(filename string, f *os.File) (Makefile, error) {
683 parser := newParser(f, filename)
684 return parser.parse()
685}
686
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +0900687func defaultMakefile() string {
Shinichiro Hamajib0d2e2f2015-05-20 16:42:59 +0900688 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
689 for _, filename := range candidates {
690 if exists(filename) {
691 return filename
692 }
693 }
694 ErrorNoLocation("no targets specified and no makefile found.")
695 panic("") // Cannot be reached.
696}
697
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900698func parseMakefileReader(rd io.Reader, name string, lineno int) (Makefile, error) {
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900699 parser := newParser(rd, name)
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900700 parser.lineno = lineno
Shinichiro Hamaji29ffc972015-04-06 16:13:27 +0900701 parser.elineno = lineno
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900702 parser.linenoFixed = true
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900703 return parser.parse()
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900704}
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900705
706func ParseMakefileString(s string, name string, lineno int) (Makefile, error) {
707 return parseMakefileReader(strings.NewReader(s), name, lineno)
708}
709
710func ParseMakefileBytes(s []byte, name string, lineno int) (Makefile, error) {
711 return parseMakefileReader(bytes.NewReader(s), name, lineno)
712}
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900713
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900714type mkCacheEntry struct {
715 mk Makefile
716 hash [sha1.Size]byte
717 err error
718 ts int64
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900719}
720
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900721type makefileCacheT struct {
722 mu sync.Mutex
723 mk map[string]mkCacheEntry
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900724}
725
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900726var makefileCache = &makefileCacheT{
727 mk: make(map[string]mkCacheEntry),
728}
729
730func (mc *makefileCacheT) lookup(filename string) (Makefile, [sha1.Size]byte, bool, error) {
731 var hash [sha1.Size]byte
732 mc.mu.Lock()
733 c, present := mc.mk[filename]
734 mc.mu.Unlock()
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900735 if !present {
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900736 return Makefile{}, hash, false, nil
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900737 }
738 ts := getTimestamp(filename)
739 if ts < 0 || ts >= c.ts {
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900740 return Makefile{}, hash, false, nil
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900741 }
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900742 return c.mk, c.hash, true, c.err
743}
744
745func (mc *makefileCacheT) parse(filename string) (Makefile, [sha1.Size]byte, error) {
746 Logf("parse Makefile %q", filename)
747 mk, hash, ok, err := makefileCache.lookup(filename)
748 if ok {
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900749 if LogFlag {
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900750 Logf("makefile cache hit for %q", filename)
751 }
752 return mk, hash, err
753 }
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900754 if LogFlag {
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900755 Logf("reading makefile %q", filename)
756 }
757 c, err := ioutil.ReadFile(filename)
758 if err != nil {
759 return Makefile{}, hash, err
760 }
761 hash = sha1.Sum(c)
762 mk, err = ParseMakefile(c, filename)
763 if err != nil {
764 return Makefile{}, hash, err
765 }
766 makefileCache.mu.Lock()
767 makefileCache.mk[filename] = mkCacheEntry{
768 mk: mk,
769 hash: hash,
770 err: err,
771 ts: time.Now().Unix(),
772 }
773 makefileCache.mu.Unlock()
774 return mk, hash, err
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900775}
776
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900777func ParseMakefile(s []byte, filename string) (Makefile, error) {
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900778 parser := newParser(bytes.NewReader(s), filename)
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900779 return parser.parse()
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900780}