blob: 603b8122bf3be4470e034dff304a0b067dbac13d [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 Ukai119dc912015-03-30 16:52:41 +090015package main
16
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 Ukaif8efa0a2015-03-30 18:10:11 +090025 "fmt"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090026 "io"
27 "os"
Shinichiro Hamajid7bef602015-03-30 19:55:32 +090028 "strings"
Shinichiro Hamaji584bb062015-06-04 13:25:13 +090029 "time"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090030)
31
32type Makefile struct {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +090033 filename string
34 stmts []AST
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090035}
36
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090037type ifState struct {
Shinichiro Hamajia06760f2015-04-07 13:13:45 +090038 ast *IfAST
39 inElse bool
40 numNest int
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090041}
42
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090043type parser struct {
Shinichiro Hamaji370be722015-04-10 14:55:23 +090044 rd *bufio.Reader
45 mk Makefile
46 lineno int
47 elineno int // lineno == elineno unless there is trailing '\'.
48 linenoFixed bool
49 unBuf []byte
50 hasUnBuf bool
51 done bool
52 outStmts *[]AST
53 ifStack []ifState
54 inDef []string
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +090055 defOpt string
Shinichiro Hamaji370be722015-04-10 14:55:23 +090056 numIfNest int
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090057}
58
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090059func newParser(rd io.Reader, filename string) *parser {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090060 p := &parser{
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +090061 rd: bufio.NewReader(rd),
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090062 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +090063 p.mk.filename = filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090064 p.outStmts = &p.mk.stmts
65 return p
66}
67
Shinichiro Hamajiae32b782015-03-31 14:41:19 +090068func (p *parser) addStatement(ast AST) {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090069 *p.outStmts = append(*p.outStmts, ast)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090070}
71
Shinichiro Hamajie1841582015-03-30 17:20:33 +090072func (p *parser) readLine() []byte {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090073 if p.hasUnBuf {
74 p.hasUnBuf = false
75 return p.unBuf
Shinichiro Hamajie1841582015-03-30 17:20:33 +090076 }
77
Shinichiro Hamaji370be722015-04-10 14:55:23 +090078 if !p.linenoFixed {
79 p.lineno = p.elineno
80 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +090081 line, err := p.rd.ReadBytes('\n')
Shinichiro Hamaji370be722015-04-10 14:55:23 +090082 if !p.linenoFixed {
83 p.lineno++
84 p.elineno = p.lineno
85 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +090086 if err == io.EOF {
87 p.done = true
88 } else if err != nil {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +090089 panic(fmt.Errorf("readline %s:%d: %v", p.mk.filename, p.lineno, err))
Shinichiro Hamajie1841582015-03-30 17:20:33 +090090 }
91
Shinichiro Hamaji56c868c2015-04-09 10:17:10 +090092 line = bytes.TrimRight(line, "\r\n")
Shinichiro Hamajie1841582015-03-30 17:20:33 +090093
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +090094 return line
95}
96
97func removeComment(line []byte) []byte {
98 var parenStack []byte
Shinichiro Hamajif4d3ee52015-04-22 16:19:33 +090099 // Do not use range as we may modify |line| and |i|.
100 for i := 0; i < len(line); i++ {
101 ch := line[i]
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900102 switch ch {
103 case '(', '{':
104 parenStack = append(parenStack, ch)
105 case ')', '}':
106 if len(parenStack) > 0 {
Shinichiro Hamajif863d862015-06-17 16:48:37 +0900107 cp := closeParen(parenStack[len(parenStack)-1])
108 if cp == ch {
109 parenStack = parenStack[:len(parenStack)-1]
110 }
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900111 }
112 case '#':
113 if len(parenStack) == 0 {
Shinichiro Hamajif4d3ee52015-04-22 16:19:33 +0900114 if i == 0 || line[i-1] != '\\' {
115 return line[:i]
116 }
117 // Drop the backslash before '#'.
118 line = append(line[:i-1], line[i:]...)
119 i--
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900120 }
121 }
122 }
123 return line
124}
125
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900126func hasTrailingBackslash(line []byte) bool {
127 if len(line) == 0 {
128 return false
129 }
130 if line[len(line)-1] != '\\' {
131 return false
132 }
133 return len(line) <= 1 || line[len(line)-2] != '\\'
134}
135
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900136func (p *parser) processDefineLine(line []byte) []byte {
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900137 for hasTrailingBackslash(line) {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900138 line = line[:len(line)-1]
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900139 line = bytes.TrimRight(line, "\t ")
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900140 lineno := p.lineno
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900141 nline := trimLeftSpaceBytes(p.readLine())
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900142 p.lineno = lineno
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900143 line = append(line, ' ')
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900144 line = append(line, nline...)
145 }
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900146 return line
147}
148
149func (p *parser) processMakefileLine(line []byte) []byte {
150 return removeComment(p.processDefineLine(line))
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900151}
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900152
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900153func (p *parser) processRecipeLine(line []byte) []byte {
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900154 for hasTrailingBackslash(line) {
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900155 line = append(line, '\n')
156 lineno := p.lineno
157 nline := p.readLine()
158 p.lineno = lineno
159 line = append(line, nline...)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900160 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900161 return line
162}
163
164func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900165 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900166 panic("unreadLine twice!")
167 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900168 p.unBuf = line
169 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900170}
171
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900172func newAssignAST(p *parser, lhsBytes []byte, rhsBytes []byte, op string) *AssignAST {
173 lhs, _, err := parseExpr(lhsBytes, nil)
174 if err != nil {
175 panic(err)
176 }
177 rhs, _, err := parseExpr(rhsBytes, nil)
178 if err != nil {
179 panic(err)
180 }
181 opt := ""
182 if p != nil {
183 opt = p.defOpt
184 }
185 return &AssignAST{
186 lhs: lhs,
187 rhs: rhs,
188 op: op,
189 opt: opt,
190 }
191}
192
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900193func (p *parser) parseAssign(line []byte, sep, esep int) AST {
Fumitoshi Ukai8fabdd02015-06-08 10:27:47 +0900194 Logf("parseAssign %q op:%q", line, line[sep:esep])
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900195 ast := newAssignAST(p, bytes.TrimSpace(line[:sep]), trimLeftSpaceBytes(line[esep:]), string(line[sep:esep]))
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900196 ast.filename = p.mk.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900197 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900198 return ast
199}
200
Shinichiro Hamaji50309a62015-06-04 14:01:55 +0900201func (p *parser) parseMaybeRule(line []byte, equalIndex, semicolonIndex int) AST {
202 if len(trimSpaceBytes(line)) == 0 {
Shinichiro Hamajide829712015-03-31 18:26:56 +0900203 return nil
204 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900205
Shinichiro Hamaji171a3df2015-06-04 14:17:45 +0900206 expr := line
207 var term byte
208 var afterTerm []byte
209
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900210 // Either '=' or ';' is used.
211 if equalIndex >= 0 && semicolonIndex >= 0 {
212 if equalIndex < semicolonIndex {
213 semicolonIndex = -1
214 } else {
215 equalIndex = -1
216 }
217 }
Shinichiro Hamaji171a3df2015-06-04 14:17:45 +0900218 if semicolonIndex >= 0 {
219 afterTerm = expr[semicolonIndex:]
220 expr = expr[0:semicolonIndex]
221 term = ';'
222 } else if equalIndex >= 0 {
223 afterTerm = expr[equalIndex:]
224 expr = expr[0:equalIndex]
225 term = '='
226 }
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900227
Shinichiro Hamaji2d4b6052015-06-04 14:20:44 +0900228 v, _, err := parseExpr(expr, nil)
229 if err != nil {
230 panic(fmt.Errorf("parse %s:%d %v", p.mk.filename, p.lineno, err))
231 }
232
Shinichiro Hamaji486e9de2015-04-09 15:20:32 +0900233 ast := &MaybeRuleAST{
Shinichiro Hamaji2d4b6052015-06-04 14:20:44 +0900234 expr: v,
Shinichiro Hamaji171a3df2015-06-04 14:17:45 +0900235 term: term,
236 afterTerm: afterTerm,
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900237 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900238 ast.filename = p.mk.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900239 ast.lineno = p.lineno
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900240 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900241}
242
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900243func (p *parser) parseInclude(line string, oplen int) AST {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900244 // TODO(ukai): parse expr here
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900245 ast := &IncludeAST{
246 expr: line[oplen+1:],
247 op: line[:oplen],
248 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900249 ast.filename = p.mk.filename
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900250 ast.lineno = p.lineno
251 return ast
252}
253
Shinichiro Hamaji61d2e112015-06-04 14:56:47 +0900254func (p *parser) parseIfdef(line []byte, oplen int) AST {
Shinichiro Hamaji72ae2dd2015-06-17 15:47:15 +0900255 lhs, _, err := parseExpr(trimLeftSpaceBytes(line[oplen+1:]), nil)
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900256 if err != nil {
257 panic(fmt.Errorf("ifdef parse %s:%d %v", p.mk.filename, p.lineno, err))
258 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900259 ast := &IfAST{
Shinichiro Hamaji61d2e112015-06-04 14:56:47 +0900260 op: string(line[:oplen]),
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900261 lhs: lhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900262 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900263 ast.filename = p.mk.filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900264 ast.lineno = p.lineno
265 p.addStatement(ast)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900266 p.ifStack = append(p.ifStack, ifState{ast: ast, numNest: p.numIfNest})
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900267 p.outStmts = &ast.trueStmts
268 return ast
269}
270
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900271func (p *parser) parseTwoQuotes(s string, op string) ([]string, bool) {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900272 var args []string
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900273 for i := 0; i < 2; i++ {
274 s = strings.TrimSpace(s)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900275 if s == "" {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900276 return nil, false
277 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900278 quote := s[0]
279 if quote != '\'' && quote != '"' {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900280 return nil, false
281 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900282 end := strings.IndexByte(s[1:], quote) + 1
283 if end < 0 {
284 return nil, false
285 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900286 args = append(args, s[1:end])
287 s = s[end+1:]
288 }
289 if len(s) > 0 {
290 Error(p.mk.filename, p.lineno, `extraneous text after %q directive`, op)
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900291 }
292 return args, true
293}
294
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900295// parse
296// "(lhs, rhs)"
297// "lhs, rhs"
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900298func (p *parser) parseEq(s string, op string) (string, string, bool) {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900299 if s[0] == '(' && s[len(s)-1] == ')' {
300 s = s[1 : len(s)-1]
301 term := []byte{','}
302 in := []byte(s)
303 v, n, err := parseExpr(in, term)
304 if err != nil {
305 return "", "", false
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900306 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900307 lhs := v.String()
308 n++
309 n += skipSpaces(in[n:], nil)
310 v, n, err = parseExpr(in[n:], nil)
311 if err != nil {
312 return "", "", false
313 }
314 rhs := v.String()
315 return lhs, rhs, true
316 }
317 args, ok := p.parseTwoQuotes(s, op)
318 if !ok {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900319 return "", "", false
320 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900321 return args[0], args[1], true
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900322}
323
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900324func (p *parser) parseIfeq(line string, oplen int) AST {
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900325 op := line[:oplen]
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900326 lhsBytes, rhsBytes, ok := p.parseEq(strings.TrimSpace(line[oplen+1:]), op)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900327 if !ok {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900328 Error(p.mk.filename, p.lineno, `*** invalid syntax in conditional.`)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900329 }
330
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900331 lhs, _, err := parseExpr([]byte(lhsBytes), nil)
332 if err != nil {
333 panic(fmt.Errorf("parse ifeq lhs %s:%d %v", p.mk.filename, p.lineno, err))
334 }
335 rhs, _, err := parseExpr([]byte(rhsBytes), nil)
336 if err != nil {
337 panic(fmt.Errorf("parse ifeq rhs %s:%d %v", p.mk.filename, p.lineno, err))
338 }
339
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900340 ast := &IfAST{
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900341 op: op,
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900342 lhs: lhs,
343 rhs: rhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900344 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900345 ast.filename = p.mk.filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900346 ast.lineno = p.lineno
347 p.addStatement(ast)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900348 p.ifStack = append(p.ifStack, ifState{ast: ast, numNest: p.numIfNest})
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900349 p.outStmts = &ast.trueStmts
350 return ast
351}
352
353func (p *parser) checkIfStack(curKeyword string) {
354 if len(p.ifStack) == 0 {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900355 Error(p.mk.filename, p.lineno, `*** extraneous %q.`, curKeyword)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900356 }
357}
358
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900359func (p *parser) parseElse(line []byte) {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900360 p.checkIfStack("else")
361 state := &p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900362 if state.inElse {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900363 Error(p.mk.filename, p.lineno, `*** only one "else" per conditional.`)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900364 }
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900365 state.inElse = true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900366 p.outStmts = &state.ast.falseStmts
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900367
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900368 nextIf := trimLeftSpaceBytes(line[len("else"):])
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900369 if len(nextIf) == 0 {
370 return
371 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900372 var ifDirectives = map[string]directiveFunc{
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900373 "ifdef ": ifdefDirective,
374 "ifndef ": ifndefDirective,
375 "ifeq ": ifeqDirective,
376 "ifneq ": ifneqDirective,
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900377 }
378 p.numIfNest = state.numNest + 1
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900379 if f, ok := p.isDirective(nextIf, ifDirectives); ok {
380 f(p, nextIf)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900381 p.numIfNest = 0
382 return
383 }
384 p.numIfNest = 0
385 WarnNoPrefix(p.mk.filename, p.lineno, "extraneous text after `else` directive")
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900386}
387
388func (p *parser) parseEndif(line string) {
389 p.checkIfStack("endif")
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900390 state := p.ifStack[len(p.ifStack)-1]
391 for t := 0; t <= state.numNest; t++ {
392 p.ifStack = p.ifStack[0 : len(p.ifStack)-1]
393 if len(p.ifStack) == 0 {
394 p.outStmts = &p.mk.stmts
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900395 } else {
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900396 state := p.ifStack[len(p.ifStack)-1]
397 if state.inElse {
398 p.outStmts = &state.ast.falseStmts
399 } else {
400 p.outStmts = &state.ast.trueStmts
401 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900402 }
403 }
404}
405
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900406type directiveFunc func(*parser, []byte) []byte
407
408var makeDirectives = map[string]directiveFunc{
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900409 "include ": includeDirective,
410 "-include ": sincludeDirective,
411 "sinclude": sincludeDirective,
412 "ifdef ": ifdefDirective,
413 "ifndef ": ifndefDirective,
414 "ifeq ": ifeqDirective,
415 "ifneq ": ifneqDirective,
416 "else": elseDirective,
417 "endif": endifDirective,
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900418 "define ": defineDirective,
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900419 "override ": overrideDirective,
420 "export ": exportDirective,
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900421 "unexport ": unexportDirective,
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900422}
423
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900424// TODO(ukai): use []byte
425func (p *parser) isDirective(line []byte, directives map[string]directiveFunc) (directiveFunc, bool) {
426 stripped := trimLeftSpaceBytes(line)
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900427 // Fast paths.
428 // TODO: Consider using a trie.
429 if len(stripped) == 0 {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900430 return nil, false
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900431 }
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900432 if ch := stripped[0]; ch != 'i' && ch != '-' && ch != 's' && ch != 'e' && ch != 'd' && ch != 'o' && ch != 'u' {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900433 return nil, false
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900434 }
435
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900436 for prefix, f := range directives {
437 if bytes.HasPrefix(stripped, []byte(prefix)) {
438 return f, true
439 }
440 if prefix[len(prefix)-1] == ' ' && bytes.HasPrefix(stripped, []byte(prefix[:len(prefix)-1])) && stripped[len(prefix)-1] == '\t' {
441 return f, true
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900442 }
443 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900444 return nil, false
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900445}
446
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900447func includeDirective(p *parser, line []byte) []byte {
448 p.addStatement(p.parseInclude(string(line), len("include")))
449 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900450}
451
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900452func sincludeDirective(p *parser, line []byte) []byte {
453 p.addStatement(p.parseInclude(string(line), len("-include")))
454 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900455}
456
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900457func ifdefDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji61d2e112015-06-04 14:56:47 +0900458 p.parseIfdef(line, len("ifdef"))
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900459 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900460}
461
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900462func ifndefDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji61d2e112015-06-04 14:56:47 +0900463 p.parseIfdef(line, len("ifndef"))
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900464 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900465}
466
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900467func ifeqDirective(p *parser, line []byte) []byte {
468 p.parseIfeq(string(line), len("ifeq"))
469 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900470}
471
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900472func ifneqDirective(p *parser, line []byte) []byte {
473 p.parseIfeq(string(line), len("ifneq"))
474 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900475}
476
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900477func elseDirective(p *parser, line []byte) []byte {
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900478 p.parseElse(line)
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900479 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900480}
481
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900482func endifDirective(p *parser, line []byte) []byte {
483 p.parseEndif(string(line))
484 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900485}
486
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900487func defineDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji72ae2dd2015-06-17 15:47:15 +0900488 lhs := trimLeftSpaceBytes(line[len("define "):])
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900489 p.inDef = []string{string(lhs)}
490 return nil
491}
492
493func overrideDirective(p *parser, line []byte) []byte {
494 p.defOpt = "override"
495 line = trimLeftSpaceBytes(line[len("override "):])
496 defineDirective := map[string]directiveFunc{
497 "define": defineDirective,
498 }
499 if f, ok := p.isDirective(line, defineDirective); ok {
500 f(p, line)
501 return nil
502 }
503 // e.g. overrider foo := bar
504 // line will be "foo := bar".
505 return line
506}
507
Shinichiro Hamajiea553f32015-05-29 17:03:33 +0900508func handleExport(p *parser, line []byte, export bool) (hasEqual bool) {
509 equalIndex := bytes.IndexByte(line, '=')
510 if equalIndex > 0 {
511 hasEqual = true
512 switch line[equalIndex-1] {
513 case ':', '+', '?':
514 equalIndex--
515 }
516 line = line[:equalIndex]
517 }
518
Shinichiro Hamajif61033d2015-05-29 15:01:48 +0900519 ast := &ExportAST{
520 expr: line,
521 export: export,
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900522 }
Shinichiro Hamajif61033d2015-05-29 15:01:48 +0900523 ast.filename = p.mk.filename
524 ast.lineno = p.lineno
525 p.addStatement(ast)
Shinichiro Hamajiea553f32015-05-29 17:03:33 +0900526 return hasEqual
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900527}
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900528
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900529func exportDirective(p *parser, line []byte) []byte {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900530 p.defOpt = "export"
531 line = trimLeftSpaceBytes(line[len("export "):])
532 defineDirective := map[string]directiveFunc{
533 "define": defineDirective,
534 }
535 if f, ok := p.isDirective(line, defineDirective); ok {
536 f(p, line)
537 return nil
538 }
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900539
Shinichiro Hamajiea553f32015-05-29 17:03:33 +0900540 if !handleExport(p, line, true) {
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900541 return nil
542 }
543
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900544 // e.g. export foo := bar
545 // line will be "foo := bar".
546 return line
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900547}
548
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900549func unexportDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900550 handleExport(p, line[len("unexport "):], false)
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900551 return nil
552}
553
Shinichiro Hamajica668572015-06-17 06:36:45 +0900554func (p *parser) isEndef(s string) bool {
555 if s == "endef" {
556 return true
557 }
558 found := strings.IndexAny(s, " \t")
559 if found >= 0 && s[:found] == "endef" {
560 rest := strings.TrimSpace(s[found+1:])
561 if rest != "" && rest[0] != '#' {
562 WarnNoPrefix(p.mk.filename, p.lineno, "extraneous text after \"endef\" directive")
563 }
564 return true
565 }
566 return false
567}
568
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900569func (p *parser) parse() (mk Makefile, err error) {
570 defer func() {
571 if r := recover(); r != nil {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +0900572 err = fmt.Errorf("panic in parse %s: %v", mk.filename, r)
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900573 }
574 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900575 for !p.done {
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900576 line := p.readLine()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900577
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900578 if len(p.inDef) > 0 {
Shinichiro Hamajica668572015-06-17 06:36:45 +0900579 lineStr := string(p.processDefineLine(line))
580 if p.isEndef(lineStr) {
Fumitoshi Ukai8fabdd02015-06-08 10:27:47 +0900581 Logf("multilineAssign %q", p.inDef)
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900582 ast := newAssignAST(p, []byte(p.inDef[0]), []byte(strings.Join(p.inDef[1:], "\n")), "=")
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900583 ast.filename = p.mk.filename
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900584 ast.lineno = p.lineno - len(p.inDef)
585 p.addStatement(ast)
586 p.inDef = nil
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900587 p.defOpt = ""
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900588 continue
589 }
Shinichiro Hamajica668572015-06-17 06:36:45 +0900590 p.inDef = append(p.inDef, lineStr)
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900591 continue
592 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900593 p.defOpt = ""
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900594
Shinichiro Hamaji960161f2015-04-13 17:10:10 +0900595 if len(bytes.TrimSpace(line)) == 0 {
596 continue
597 }
598
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900599 if f, ok := p.isDirective(line, makeDirectives); ok {
Shinichiro Hamaji72ae2dd2015-06-17 15:47:15 +0900600 line = trimSpaceBytes(p.processMakefileLine(line))
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900601 line = f(p, line)
602 if len(line) == 0 {
603 continue
604 }
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900605 }
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900606 if line[0] == '\t' {
607 ast := &CommandAST{cmd: string(p.processRecipeLine(line[1:]))}
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900608 ast.filename = p.mk.filename
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900609 ast.lineno = p.lineno
610 p.addStatement(ast)
611 continue
612 }
613
614 line = p.processMakefileLine(line)
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900615
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900616 var ast AST
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900617 var parenStack []byte
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900618 equalIndex := -1
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900619 semicolonIndex := -1
620 isRule := false
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900621 for i, ch := range line {
622 switch ch {
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900623 case '(', '{':
624 parenStack = append(parenStack, ch)
625 case ')', '}':
626 if len(parenStack) == 0 {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900627 Warn(p.mk.filename, p.lineno, "Unmatched parens: %s", line)
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900628 } else {
Shinichiro Hamajif863d862015-06-17 16:48:37 +0900629 cp := closeParen(parenStack[len(parenStack)-1])
630 if cp == ch {
631 parenStack = parenStack[:len(parenStack)-1]
632 }
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900633 }
634 }
635 if len(parenStack) > 0 {
636 continue
637 }
638
639 switch ch {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900640 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900641 if i+1 < len(line) && line[i+1] == '=' {
Shinichiro Hamaji156ef3e2015-04-11 22:44:41 +0900642 if !isRule {
643 ast = p.parseAssign(line, i, i+2)
644 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900645 } else {
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900646 isRule = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900647 }
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900648 case ';':
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900649 if semicolonIndex < 0 {
650 semicolonIndex = i
651 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900652 case '=':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900653 if !isRule {
654 ast = p.parseAssign(line, i, i+1)
655 }
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900656 if equalIndex < 0 {
657 equalIndex = i
658 }
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900659 case '?', '+':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900660 if !isRule && i+1 < len(line) && line[i+1] == '=' {
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900661 ast = p.parseAssign(line, i, i+2)
662 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900663 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900664 if ast != nil {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900665 p.addStatement(ast)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900666 break
667 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900668 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900669 if ast == nil {
Shinichiro Hamaji50309a62015-06-04 14:01:55 +0900670 ast = p.parseMaybeRule(line, equalIndex, semicolonIndex)
Shinichiro Hamajide829712015-03-31 18:26:56 +0900671 if ast != nil {
672 p.addStatement(ast)
673 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900674 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900675 }
676 return p.mk, nil
677}
678
Shinichiro Hamaji290eb252015-05-19 18:03:19 +0900679func ParseMakefileFd(filename string, f *os.File) (Makefile, error) {
680 parser := newParser(f, filename)
681 return parser.parse()
682}
683
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900684/*
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900685func ParseMakefile(filename string) (Makefile, error) {
Fumitoshi Ukai8fabdd02015-06-08 10:27:47 +0900686 Logf("ParseMakefile %q", filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900687 f, err := os.Open(filename)
688 if err != nil {
689 return Makefile{}, err
690 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900691 defer f.Close()
Shinichiro Hamaji290eb252015-05-19 18:03:19 +0900692 return ParseMakefileFd(filename, f)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900693}
694
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900695func ParseDefaultMakefile() (Makefile, string, error) {
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900696 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
697 for _, filename := range candidates {
698 if exists(filename) {
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900699 mk, err := ParseMakefile(filename)
700 return mk, filename, err
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900701 }
702 }
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900703 return Makefile{}, "", errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900704}
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900705*/
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900706
Shinichiro Hamajib0d2e2f2015-05-20 16:42:59 +0900707func GetDefaultMakefile() string {
708 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
709 for _, filename := range candidates {
710 if exists(filename) {
711 return filename
712 }
713 }
714 ErrorNoLocation("no targets specified and no makefile found.")
715 panic("") // Cannot be reached.
716}
717
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900718func parseMakefileReader(rd io.Reader, name string, lineno int) (Makefile, error) {
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900719 parser := newParser(rd, name)
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900720 parser.lineno = lineno
Shinichiro Hamaji29ffc972015-04-06 16:13:27 +0900721 parser.elineno = lineno
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900722 parser.linenoFixed = true
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900723 return parser.parse()
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900724}
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900725
726func ParseMakefileString(s string, name string, lineno int) (Makefile, error) {
727 return parseMakefileReader(strings.NewReader(s), name, lineno)
728}
729
730func ParseMakefileBytes(s []byte, name string, lineno int) (Makefile, error) {
731 return parseMakefileReader(bytes.NewReader(s), name, lineno)
732}
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900733
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900734type MakefileCache struct {
735 mk Makefile
736 err error
737 ts int64
738}
739
740var makefileCache map[string]MakefileCache
741
742func InitMakefileCache() {
743 if makefileCache == nil {
744 makefileCache = make(map[string]MakefileCache)
745 }
746}
747
Fumitoshi Ukai936de102015-06-08 11:21:16 +0900748func LookupMakefileCache(filename string) (Makefile, bool, error) {
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900749 c, present := makefileCache[filename]
750 if !present {
Fumitoshi Ukai936de102015-06-08 11:21:16 +0900751 return Makefile{}, false, nil
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900752 }
753 ts := getTimestamp(filename)
754 if ts < 0 || ts >= c.ts {
Fumitoshi Ukai936de102015-06-08 11:21:16 +0900755 return Makefile{}, false, nil
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900756 }
Fumitoshi Ukai8fabdd02015-06-08 10:27:47 +0900757 Logf("Cache hit for %q", filename)
Fumitoshi Ukai936de102015-06-08 11:21:16 +0900758 return c.mk, true, c.err
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900759}
760
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900761func ParseMakefile(s []byte, filename string) (Makefile, error) {
Fumitoshi Ukai8fabdd02015-06-08 10:27:47 +0900762 Logf("ParseMakefile %q", filename)
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900763 parser := newParser(bytes.NewReader(s), filename)
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900764 mk, err := parser.parse()
765 makefileCache[filename] = MakefileCache{
766 mk: mk,
767 err: err,
768 ts: time.Now().Unix(),
769 }
770 return mk, err
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900771}