blob: 06e5f7474abdc4350f12679d5735e9fd94f7ec61 [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"
Shinichiro Hamajid7bef602015-03-30 19:55:32 +090029 "strings"
Fumitoshi Ukai9042b992015-06-23 16:10:27 +090030 "sync"
Shinichiro Hamaji584bb062015-06-04 13:25:13 +090031 "time"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090032)
33
Fumitoshi Ukai08eda652015-06-25 16:27:10 +090034type makefile struct {
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +090035 filename string
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +090036 stmts []ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090037}
38
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090039type ifState struct {
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +090040 ast *ifAST
Shinichiro Hamajia06760f2015-04-07 13:13:45 +090041 inElse bool
42 numNest int
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090043}
44
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090045type parser struct {
Shinichiro Hamaji370be722015-04-10 14:55:23 +090046 rd *bufio.Reader
Fumitoshi Ukai08eda652015-06-25 16:27:10 +090047 mk makefile
Shinichiro Hamaji370be722015-04-10 14:55:23 +090048 lineno int
49 elineno int // lineno == elineno unless there is trailing '\'.
50 linenoFixed bool
51 unBuf []byte
52 hasUnBuf bool
53 done bool
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +090054 outStmts *[]ast
Shinichiro Hamaji370be722015-04-10 14:55:23 +090055 ifStack []ifState
56 inDef []string
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +090057 defOpt string
Shinichiro Hamaji370be722015-04-10 14:55:23 +090058 numIfNest int
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090059}
60
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090061func newParser(rd io.Reader, filename string) *parser {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090062 p := &parser{
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +090063 rd: bufio.NewReader(rd),
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090064 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +090065 p.mk.filename = filename
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090066 p.outStmts = &p.mk.stmts
67 return p
68}
69
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +090070func (p *parser) addStatement(stmt ast) {
71 *p.outStmts = append(*p.outStmts, stmt)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090072}
73
Shinichiro Hamajie1841582015-03-30 17:20:33 +090074func (p *parser) readLine() []byte {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090075 if p.hasUnBuf {
76 p.hasUnBuf = false
77 return p.unBuf
Shinichiro Hamajie1841582015-03-30 17:20:33 +090078 }
79
Shinichiro Hamaji370be722015-04-10 14:55:23 +090080 if !p.linenoFixed {
81 p.lineno = p.elineno
82 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +090083 line, err := p.rd.ReadBytes('\n')
Shinichiro Hamaji370be722015-04-10 14:55:23 +090084 if !p.linenoFixed {
85 p.lineno++
86 p.elineno = p.lineno
87 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +090088 if err == io.EOF {
89 p.done = true
90 } else if err != nil {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +090091 panic(fmt.Errorf("readline %s:%d: %v", p.mk.filename, p.lineno, err))
Shinichiro Hamajie1841582015-03-30 17:20:33 +090092 }
93
Shinichiro Hamaji56c868c2015-04-09 10:17:10 +090094 line = bytes.TrimRight(line, "\r\n")
Shinichiro Hamajie1841582015-03-30 17:20:33 +090095
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +090096 return line
97}
98
99func removeComment(line []byte) []byte {
100 var parenStack []byte
Shinichiro Hamajif4d3ee52015-04-22 16:19:33 +0900101 // Do not use range as we may modify |line| and |i|.
102 for i := 0; i < len(line); i++ {
103 ch := line[i]
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900104 switch ch {
105 case '(', '{':
106 parenStack = append(parenStack, ch)
107 case ')', '}':
108 if len(parenStack) > 0 {
Shinichiro Hamajif863d862015-06-17 16:48:37 +0900109 cp := closeParen(parenStack[len(parenStack)-1])
110 if cp == ch {
111 parenStack = parenStack[:len(parenStack)-1]
112 }
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900113 }
114 case '#':
115 if len(parenStack) == 0 {
Shinichiro Hamajif4d3ee52015-04-22 16:19:33 +0900116 if i == 0 || line[i-1] != '\\' {
117 return line[:i]
118 }
119 // Drop the backslash before '#'.
120 line = append(line[:i-1], line[i:]...)
121 i--
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900122 }
123 }
124 }
125 return line
126}
127
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900128func hasTrailingBackslash(line []byte) bool {
129 if len(line) == 0 {
130 return false
131 }
132 if line[len(line)-1] != '\\' {
133 return false
134 }
135 return len(line) <= 1 || line[len(line)-2] != '\\'
136}
137
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900138func (p *parser) processDefineLine(line []byte) []byte {
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900139 for hasTrailingBackslash(line) {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900140 line = line[:len(line)-1]
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900141 line = bytes.TrimRight(line, "\t ")
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900142 lineno := p.lineno
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900143 nline := trimLeftSpaceBytes(p.readLine())
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900144 p.lineno = lineno
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900145 line = append(line, ' ')
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900146 line = append(line, nline...)
147 }
Shinichiro Hamajie52c16c2015-04-11 23:41:39 +0900148 return line
149}
150
151func (p *parser) processMakefileLine(line []byte) []byte {
152 return removeComment(p.processDefineLine(line))
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900153}
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900154
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900155func (p *parser) processRecipeLine(line []byte) []byte {
Shinichiro Hamajie2f6e902015-04-22 16:08:47 +0900156 for hasTrailingBackslash(line) {
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900157 line = append(line, '\n')
158 lineno := p.lineno
159 nline := p.readLine()
160 p.lineno = lineno
161 line = append(line, nline...)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900162 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900163 return line
164}
165
166func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900167 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900168 panic("unreadLine twice!")
169 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900170 p.unBuf = line
171 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900172}
173
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900174func newAssignAST(p *parser, lhsBytes []byte, rhsBytes []byte, op string) *assignAST {
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900175 lhs, _, err := parseExpr(lhsBytes, nil, true)
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900176 if err != nil {
177 panic(err)
178 }
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900179 rhs, _, err := parseExpr(rhsBytes, nil, true)
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900180 if err != nil {
181 panic(err)
182 }
183 opt := ""
184 if p != nil {
185 opt = p.defOpt
186 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900187 return &assignAST{
Shinichiro Hamaji7825b652015-06-04 13:47:14 +0900188 lhs: lhs,
189 rhs: rhs,
190 op: op,
191 opt: opt,
192 }
193}
194
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900195func (p *parser) parseAssign(line []byte, sep, esep int) ast {
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900196 logf("parseAssign %q op:%q", line, line[sep:esep])
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900197 aast := newAssignAST(p, bytes.TrimSpace(line[:sep]), trimLeftSpaceBytes(line[esep:]), string(line[sep:esep]))
198 aast.filename = p.mk.filename
199 aast.lineno = p.lineno
200 return aast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900201}
202
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900203func (p *parser) parseMaybeRule(line []byte, equalIndex, semicolonIndex int) ast {
Shinichiro Hamaji50309a62015-06-04 14:01:55 +0900204 if len(trimSpaceBytes(line)) == 0 {
Shinichiro Hamajide829712015-03-31 18:26:56 +0900205 return nil
206 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900207
Shinichiro Hamaji171a3df2015-06-04 14:17:45 +0900208 expr := line
209 var term byte
210 var afterTerm []byte
211
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900212 // Either '=' or ';' is used.
213 if equalIndex >= 0 && semicolonIndex >= 0 {
214 if equalIndex < semicolonIndex {
215 semicolonIndex = -1
216 } else {
217 equalIndex = -1
218 }
219 }
Shinichiro Hamaji171a3df2015-06-04 14:17:45 +0900220 if semicolonIndex >= 0 {
221 afterTerm = expr[semicolonIndex:]
222 expr = expr[0:semicolonIndex]
223 term = ';'
224 } else if equalIndex >= 0 {
225 afterTerm = expr[equalIndex:]
226 expr = expr[0:equalIndex]
227 term = '='
228 }
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900229
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900230 v, _, err := parseExpr(expr, nil, true)
Shinichiro Hamaji2d4b6052015-06-04 14:20:44 +0900231 if err != nil {
232 panic(fmt.Errorf("parse %s:%d %v", p.mk.filename, p.lineno, err))
233 }
234
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900235 rast := &maybeRuleAST{
Shinichiro Hamaji2d4b6052015-06-04 14:20:44 +0900236 expr: v,
Shinichiro Hamaji171a3df2015-06-04 14:17:45 +0900237 term: term,
238 afterTerm: afterTerm,
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900239 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900240 rast.filename = p.mk.filename
241 rast.lineno = p.lineno
242 return rast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900243}
244
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900245func (p *parser) parseInclude(line string, oplen int) ast {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900246 // TODO(ukai): parse expr here
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900247 iast := &includeAST{
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900248 expr: line[oplen+1:],
249 op: line[:oplen],
250 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900251 iast.filename = p.mk.filename
252 iast.lineno = p.lineno
253 return iast
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900254}
255
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900256func (p *parser) parseIfdef(line []byte, oplen int) ast {
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900257 lhs, _, err := parseExpr(trimLeftSpaceBytes(line[oplen+1:]), nil, true)
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900258 if err != nil {
259 panic(fmt.Errorf("ifdef parse %s:%d %v", p.mk.filename, p.lineno, err))
260 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900261 iast := &ifAST{
Shinichiro Hamaji61d2e112015-06-04 14:56:47 +0900262 op: string(line[:oplen]),
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900263 lhs: lhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900264 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900265 iast.filename = p.mk.filename
266 iast.lineno = p.lineno
267 p.addStatement(iast)
268 p.ifStack = append(p.ifStack, ifState{ast: iast, numNest: p.numIfNest})
269 p.outStmts = &iast.trueStmts
270 return iast
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900271}
272
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900273func (p *parser) parseTwoQuotes(s string, op string) ([]string, bool) {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900274 var args []string
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900275 for i := 0; i < 2; i++ {
276 s = strings.TrimSpace(s)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900277 if s == "" {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900278 return nil, false
279 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900280 quote := s[0]
281 if quote != '\'' && quote != '"' {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900282 return nil, false
283 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900284 end := strings.IndexByte(s[1:], quote) + 1
285 if end < 0 {
286 return nil, false
287 }
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900288 args = append(args, s[1:end])
289 s = s[end+1:]
290 }
291 if len(s) > 0 {
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900292 errorExit(p.mk.filename, p.lineno, `extraneous text after %q directive`, op)
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900293 }
294 return args, true
295}
296
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900297// parse
298// "(lhs, rhs)"
299// "lhs, rhs"
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900300func (p *parser) parseEq(s string, op string) (string, string, bool) {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900301 if s[0] == '(' && s[len(s)-1] == ')' {
302 s = s[1 : len(s)-1]
303 term := []byte{','}
304 in := []byte(s)
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900305 v, n, err := parseExpr(in, term, false)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900306 if err != nil {
307 return "", "", false
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900308 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900309 lhs := v.String()
310 n++
311 n += skipSpaces(in[n:], nil)
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900312 v, n, err = parseExpr(in[n:], nil, false)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900313 if err != nil {
314 return "", "", false
315 }
316 rhs := v.String()
317 return lhs, rhs, true
318 }
319 args, ok := p.parseTwoQuotes(s, op)
320 if !ok {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900321 return "", "", false
322 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900323 return args[0], args[1], true
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900324}
325
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900326func (p *parser) parseIfeq(line string, oplen int) ast {
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900327 op := line[:oplen]
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900328 lhsBytes, rhsBytes, ok := p.parseEq(strings.TrimSpace(line[oplen+1:]), op)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900329 if !ok {
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900330 errorExit(p.mk.filename, p.lineno, `*** invalid syntax in conditional.`)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900331 }
332
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900333 lhs, _, err := parseExpr([]byte(lhsBytes), nil, true)
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900334 if err != nil {
335 panic(fmt.Errorf("parse ifeq lhs %s:%d %v", p.mk.filename, p.lineno, err))
336 }
Fumitoshi Ukai7c9aa9f2015-06-12 23:51:38 +0900337 rhs, _, err := parseExpr([]byte(rhsBytes), nil, true)
Shinichiro Hamaji1a68fd22015-06-04 14:46:56 +0900338 if err != nil {
339 panic(fmt.Errorf("parse ifeq rhs %s:%d %v", p.mk.filename, p.lineno, err))
340 }
341
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900342 iast := &ifAST{
Shinichiro Hamaji1f476382015-04-09 14:46:04 +0900343 op: op,
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900344 lhs: lhs,
345 rhs: rhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900346 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900347 iast.filename = p.mk.filename
348 iast.lineno = p.lineno
349 p.addStatement(iast)
350 p.ifStack = append(p.ifStack, ifState{ast: iast, numNest: p.numIfNest})
351 p.outStmts = &iast.trueStmts
352 return iast
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900353}
354
355func (p *parser) checkIfStack(curKeyword string) {
356 if len(p.ifStack) == 0 {
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900357 errorExit(p.mk.filename, p.lineno, `*** extraneous %q.`, curKeyword)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900358 }
359}
360
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900361func (p *parser) parseElse(line []byte) {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900362 p.checkIfStack("else")
363 state := &p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900364 if state.inElse {
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900365 errorExit(p.mk.filename, p.lineno, `*** only one "else" per conditional.`)
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900366 }
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900367 state.inElse = true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900368 p.outStmts = &state.ast.falseStmts
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900369
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900370 nextIf := trimLeftSpaceBytes(line[len("else"):])
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900371 if len(nextIf) == 0 {
372 return
373 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900374 var ifDirectives = map[string]directiveFunc{
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900375 "ifdef ": ifdefDirective,
376 "ifndef ": ifndefDirective,
377 "ifeq ": ifeqDirective,
378 "ifneq ": ifneqDirective,
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900379 }
380 p.numIfNest = state.numNest + 1
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900381 if f, ok := p.isDirective(nextIf, ifDirectives); ok {
382 f(p, nextIf)
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900383 p.numIfNest = 0
384 return
385 }
386 p.numIfNest = 0
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900387 warnNoPrefix(p.mk.filename, p.lineno, "extraneous text after `else` directive")
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900388}
389
390func (p *parser) parseEndif(line string) {
391 p.checkIfStack("endif")
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900392 state := p.ifStack[len(p.ifStack)-1]
393 for t := 0; t <= state.numNest; t++ {
394 p.ifStack = p.ifStack[0 : len(p.ifStack)-1]
395 if len(p.ifStack) == 0 {
396 p.outStmts = &p.mk.stmts
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900397 } else {
Shinichiro Hamajia06760f2015-04-07 13:13:45 +0900398 state := p.ifStack[len(p.ifStack)-1]
399 if state.inElse {
400 p.outStmts = &state.ast.falseStmts
401 } else {
402 p.outStmts = &state.ast.trueStmts
403 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900404 }
405 }
406}
407
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900408type directiveFunc func(*parser, []byte) []byte
409
410var makeDirectives = map[string]directiveFunc{
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900411 "include ": includeDirective,
412 "-include ": sincludeDirective,
413 "sinclude": sincludeDirective,
414 "ifdef ": ifdefDirective,
415 "ifndef ": ifndefDirective,
416 "ifeq ": ifeqDirective,
417 "ifneq ": ifneqDirective,
418 "else": elseDirective,
419 "endif": endifDirective,
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900420 "define ": defineDirective,
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900421 "override ": overrideDirective,
422 "export ": exportDirective,
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900423 "unexport ": unexportDirective,
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900424}
425
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900426// TODO(ukai): use []byte
427func (p *parser) isDirective(line []byte, directives map[string]directiveFunc) (directiveFunc, bool) {
428 stripped := trimLeftSpaceBytes(line)
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900429 // Fast paths.
430 // TODO: Consider using a trie.
431 if len(stripped) == 0 {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900432 return nil, false
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900433 }
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900434 if ch := stripped[0]; ch != 'i' && ch != '-' && ch != 's' && ch != 'e' && ch != 'd' && ch != 'o' && ch != 'u' {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900435 return nil, false
Shinichiro Hamajie103f652015-04-11 19:49:51 +0900436 }
437
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900438 for prefix, f := range directives {
439 if bytes.HasPrefix(stripped, []byte(prefix)) {
440 return f, true
441 }
442 if prefix[len(prefix)-1] == ' ' && bytes.HasPrefix(stripped, []byte(prefix[:len(prefix)-1])) && stripped[len(prefix)-1] == '\t' {
443 return f, true
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900444 }
445 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900446 return nil, false
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900447}
448
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900449func includeDirective(p *parser, line []byte) []byte {
450 p.addStatement(p.parseInclude(string(line), len("include")))
451 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900452}
453
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900454func sincludeDirective(p *parser, line []byte) []byte {
455 p.addStatement(p.parseInclude(string(line), len("-include")))
456 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900457}
458
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900459func ifdefDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji61d2e112015-06-04 14:56:47 +0900460 p.parseIfdef(line, len("ifdef"))
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900461 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900462}
463
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900464func ifndefDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji61d2e112015-06-04 14:56:47 +0900465 p.parseIfdef(line, len("ifndef"))
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900466 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900467}
468
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900469func ifeqDirective(p *parser, line []byte) []byte {
470 p.parseIfeq(string(line), len("ifeq"))
471 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900472}
473
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900474func ifneqDirective(p *parser, line []byte) []byte {
475 p.parseIfeq(string(line), len("ifneq"))
476 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900477}
478
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900479func elseDirective(p *parser, line []byte) []byte {
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900480 p.parseElse(line)
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900481 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900482}
483
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900484func endifDirective(p *parser, line []byte) []byte {
485 p.parseEndif(string(line))
486 return nil
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900487}
488
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900489func defineDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji72ae2dd2015-06-17 15:47:15 +0900490 lhs := trimLeftSpaceBytes(line[len("define "):])
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900491 p.inDef = []string{string(lhs)}
492 return nil
493}
494
495func overrideDirective(p *parser, line []byte) []byte {
496 p.defOpt = "override"
497 line = trimLeftSpaceBytes(line[len("override "):])
498 defineDirective := map[string]directiveFunc{
499 "define": defineDirective,
500 }
501 if f, ok := p.isDirective(line, defineDirective); ok {
502 f(p, line)
503 return nil
504 }
505 // e.g. overrider foo := bar
506 // line will be "foo := bar".
507 return line
508}
509
Shinichiro Hamajiea553f32015-05-29 17:03:33 +0900510func handleExport(p *parser, line []byte, export bool) (hasEqual bool) {
511 equalIndex := bytes.IndexByte(line, '=')
512 if equalIndex > 0 {
513 hasEqual = true
514 switch line[equalIndex-1] {
515 case ':', '+', '?':
516 equalIndex--
517 }
518 line = line[:equalIndex]
519 }
520
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900521 east := &exportAST{
Shinichiro Hamajif61033d2015-05-29 15:01:48 +0900522 expr: line,
523 export: export,
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900524 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900525 east.filename = p.mk.filename
526 east.lineno = p.lineno
527 p.addStatement(east)
Shinichiro Hamajiea553f32015-05-29 17:03:33 +0900528 return hasEqual
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900529}
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900530
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900531func exportDirective(p *parser, line []byte) []byte {
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900532 p.defOpt = "export"
533 line = trimLeftSpaceBytes(line[len("export "):])
534 defineDirective := map[string]directiveFunc{
535 "define": defineDirective,
536 }
537 if f, ok := p.isDirective(line, defineDirective); ok {
538 f(p, line)
539 return nil
540 }
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900541
Shinichiro Hamajiea553f32015-05-29 17:03:33 +0900542 if !handleExport(p, line, true) {
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900543 return nil
544 }
545
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900546 // e.g. export foo := bar
547 // line will be "foo := bar".
548 return line
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900549}
550
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900551func unexportDirective(p *parser, line []byte) []byte {
Shinichiro Hamaji7e521422015-05-29 14:23:30 +0900552 handleExport(p, line[len("unexport "):], false)
Shinichiro Hamaji07e76d52015-05-26 18:22:31 +0900553 return nil
554}
555
Shinichiro Hamajica668572015-06-17 06:36:45 +0900556func (p *parser) isEndef(s string) bool {
557 if s == "endef" {
558 return true
559 }
560 found := strings.IndexAny(s, " \t")
561 if found >= 0 && s[:found] == "endef" {
562 rest := strings.TrimSpace(s[found+1:])
563 if rest != "" && rest[0] != '#' {
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900564 warnNoPrefix(p.mk.filename, p.lineno, "extraneous text after \"endef\" directive")
Shinichiro Hamajica668572015-06-17 06:36:45 +0900565 }
566 return true
567 }
568 return false
569}
570
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900571func (p *parser) parse() (mk makefile, err error) {
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900572 defer func() {
573 if r := recover(); r != nil {
Fumitoshi Ukaia9e51362015-04-17 10:26:00 +0900574 err = fmt.Errorf("panic in parse %s: %v", mk.filename, r)
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900575 }
576 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900577 for !p.done {
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900578 line := p.readLine()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900579
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900580 if len(p.inDef) > 0 {
Shinichiro Hamajica668572015-06-17 06:36:45 +0900581 lineStr := string(p.processDefineLine(line))
582 if p.isEndef(lineStr) {
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900583 logf("multilineAssign %q", p.inDef)
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900584 aast := newAssignAST(p, []byte(p.inDef[0]), []byte(strings.Join(p.inDef[1:], "\n")), "=")
585 aast.filename = p.mk.filename
586 aast.lineno = p.lineno - len(p.inDef)
587 p.addStatement(aast)
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900588 p.inDef = nil
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900589 p.defOpt = ""
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900590 continue
591 }
Shinichiro Hamajica668572015-06-17 06:36:45 +0900592 p.inDef = append(p.inDef, lineStr)
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900593 continue
594 }
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900595 p.defOpt = ""
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900596
Shinichiro Hamaji960161f2015-04-13 17:10:10 +0900597 if len(bytes.TrimSpace(line)) == 0 {
598 continue
599 }
600
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900601 if f, ok := p.isDirective(line, makeDirectives); ok {
Shinichiro Hamaji72ae2dd2015-06-17 15:47:15 +0900602 line = trimSpaceBytes(p.processMakefileLine(line))
Fumitoshi Ukaib2c300f2015-04-23 00:59:26 +0900603 line = f(p, line)
604 if len(line) == 0 {
605 continue
606 }
Shinichiro Hamaji74b8cb52015-04-08 19:47:43 +0900607 }
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900608 if line[0] == '\t' {
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900609 cast := &commandAST{cmd: string(p.processRecipeLine(line[1:]))}
610 cast.filename = p.mk.filename
611 cast.lineno = p.lineno
612 p.addStatement(cast)
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900613 continue
614 }
615
616 line = p.processMakefileLine(line)
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900617
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900618 var stmt ast
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900619 var parenStack []byte
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900620 equalIndex := -1
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900621 semicolonIndex := -1
622 isRule := false
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900623 for i, ch := range line {
624 switch ch {
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900625 case '(', '{':
626 parenStack = append(parenStack, ch)
627 case ')', '}':
628 if len(parenStack) == 0 {
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900629 warn(p.mk.filename, p.lineno, "Unmatched parens: %s", line)
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900630 } else {
Shinichiro Hamajif863d862015-06-17 16:48:37 +0900631 cp := closeParen(parenStack[len(parenStack)-1])
632 if cp == ch {
633 parenStack = parenStack[:len(parenStack)-1]
634 }
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900635 }
636 }
637 if len(parenStack) > 0 {
638 continue
639 }
640
641 switch ch {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900642 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900643 if i+1 < len(line) && line[i+1] == '=' {
Shinichiro Hamaji156ef3e2015-04-11 22:44:41 +0900644 if !isRule {
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900645 stmt = p.parseAssign(line, i, i+2)
Shinichiro Hamaji156ef3e2015-04-11 22:44:41 +0900646 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900647 } else {
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900648 isRule = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900649 }
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900650 case ';':
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900651 if semicolonIndex < 0 {
652 semicolonIndex = i
653 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900654 case '=':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900655 if !isRule {
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900656 stmt = p.parseAssign(line, i, i+1)
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900657 }
Shinichiro Hamajie12e24d2015-04-11 23:09:20 +0900658 if equalIndex < 0 {
659 equalIndex = i
660 }
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900661 case '?', '+':
Shinichiro Hamaji3fab47e2015-04-08 18:34:41 +0900662 if !isRule && i+1 < len(line) && line[i+1] == '=' {
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900663 stmt = p.parseAssign(line, i, i+2)
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900664 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900665 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900666 if stmt != nil {
667 p.addStatement(stmt)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900668 break
669 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900670 }
Fumitoshi Ukai91ed5d72015-06-25 13:08:09 +0900671 if stmt == nil {
672 stmt = p.parseMaybeRule(line, equalIndex, semicolonIndex)
673 if stmt != nil {
674 p.addStatement(stmt)
Shinichiro Hamajide829712015-03-31 18:26:56 +0900675 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900676 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900677 }
678 return p.mk, nil
679}
680
Fumitoshi Ukai3ec25b52015-06-25 15:39:35 +0900681func defaultMakefile() string {
Shinichiro Hamajib0d2e2f2015-05-20 16:42:59 +0900682 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
683 for _, filename := range candidates {
684 if exists(filename) {
685 return filename
686 }
687 }
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900688 errorNoLocationExit("no targets specified and no makefile found.")
Shinichiro Hamajib0d2e2f2015-05-20 16:42:59 +0900689 panic("") // Cannot be reached.
690}
691
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900692func parseMakefileReader(rd io.Reader, name string, lineno int) (makefile, error) {
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900693 parser := newParser(rd, name)
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900694 parser.lineno = lineno
Shinichiro Hamaji29ffc972015-04-06 16:13:27 +0900695 parser.elineno = lineno
Shinichiro Hamaji370be722015-04-10 14:55:23 +0900696 parser.linenoFixed = true
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900697 return parser.parse()
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900698}
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900699
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900700func parseMakefileString(s string, name string, lineno int) (makefile, error) {
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900701 return parseMakefileReader(strings.NewReader(s), name, lineno)
702}
703
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900704func parseMakefileBytes(s []byte, name string, lineno int) (makefile, error) {
Shinichiro Hamaji28ea5bc2015-04-11 12:41:08 +0900705 return parseMakefileReader(bytes.NewReader(s), name, lineno)
706}
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900707
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900708type mkCacheEntry struct {
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900709 mk makefile
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900710 hash [sha1.Size]byte
711 err error
712 ts int64
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900713}
714
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900715type makefileCacheT struct {
716 mu sync.Mutex
717 mk map[string]mkCacheEntry
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900718}
719
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900720var makefileCache = &makefileCacheT{
721 mk: make(map[string]mkCacheEntry),
722}
723
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900724func (mc *makefileCacheT) lookup(filename string) (makefile, [sha1.Size]byte, bool, error) {
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900725 var hash [sha1.Size]byte
726 mc.mu.Lock()
727 c, present := mc.mk[filename]
728 mc.mu.Unlock()
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900729 if !present {
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900730 return makefile{}, hash, false, nil
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900731 }
732 ts := getTimestamp(filename)
733 if ts < 0 || ts >= c.ts {
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900734 return makefile{}, hash, false, nil
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900735 }
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900736 return c.mk, c.hash, true, c.err
737}
738
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900739func (mc *makefileCacheT) parse(filename string) (makefile, [sha1.Size]byte, error) {
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900740 logf("parse Makefile %q", filename)
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900741 mk, hash, ok, err := makefileCache.lookup(filename)
742 if ok {
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900743 if LogFlag {
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900744 logf("makefile cache hit for %q", filename)
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900745 }
746 return mk, hash, err
747 }
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900748 if LogFlag {
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +0900749 logf("reading makefile %q", filename)
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900750 }
751 c, err := ioutil.ReadFile(filename)
752 if err != nil {
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900753 return makefile{}, hash, err
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900754 }
755 hash = sha1.Sum(c)
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900756 mk, err = parseMakefile(c, filename)
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900757 if err != nil {
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900758 return makefile{}, hash, err
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900759 }
760 makefileCache.mu.Lock()
761 makefileCache.mk[filename] = mkCacheEntry{
762 mk: mk,
763 hash: hash,
764 err: err,
765 ts: time.Now().Unix(),
766 }
767 makefileCache.mu.Unlock()
768 return mk, hash, err
Shinichiro Hamaji584bb062015-06-04 13:25:13 +0900769}
770
Fumitoshi Ukai08eda652015-06-25 16:27:10 +0900771func parseMakefile(s []byte, filename string) (makefile, error) {
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900772 parser := newParser(bytes.NewReader(s), filename)
Fumitoshi Ukai9042b992015-06-23 16:10:27 +0900773 return parser.parse()
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900774}