blob: ae440952c727280c0e436c59da61e5d8a1c5a341 [file] [log] [blame]
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09001package main
2
3import (
4 "bufio"
Shinichiro Hamajie1841582015-03-30 17:20:33 +09005 "bytes"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09006 "errors"
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +09007 "fmt"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09008 "io"
9 "os"
Shinichiro Hamajid7bef602015-03-30 19:55:32 +090010 "strings"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090011)
12
13type Makefile struct {
14 stmts []AST
15}
16
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090017type ifState struct {
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +090018 ast *IfAST
19 inElse bool
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090020}
21
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090022type parser struct {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090023 rd *bufio.Reader
24 mk Makefile
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090025 filename string
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090026 lineno int
27 elineno int // lineno == elineno unless there is trailing '\'.
28 unBuf []byte
29 hasUnBuf bool
30 done bool
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090031 outStmts *[]AST
32 ifStack []ifState
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +090033 inDef []string
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090034}
35
36func exists(filename string) bool {
37 f, err := os.Open(filename)
38 if err != nil {
39 return false
40 }
41 f.Close()
42 return true
43}
44
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090045func newParser(rd io.Reader, filename string) *parser {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090046 p := &parser{
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090047 rd: bufio.NewReader(rd),
48 filename: filename,
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090049 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090050 p.outStmts = &p.mk.stmts
51 return p
52}
53
Shinichiro Hamajiae32b782015-03-31 14:41:19 +090054func (p *parser) addStatement(ast AST) {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +090055 *p.outStmts = append(*p.outStmts, ast)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090056}
57
Shinichiro Hamajie1841582015-03-30 17:20:33 +090058func (p *parser) readLine() []byte {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090059 if p.hasUnBuf {
60 p.hasUnBuf = false
61 return p.unBuf
Shinichiro Hamajie1841582015-03-30 17:20:33 +090062 }
63
64 p.lineno = p.elineno
65 line, err := p.rd.ReadBytes('\n')
66 p.lineno++
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +090067 p.elineno = p.lineno
Shinichiro Hamajie1841582015-03-30 17:20:33 +090068 if err == io.EOF {
69 p.done = true
70 } else if err != nil {
71 panic(err)
72 }
73
Shinichiro Hamajibc702b02015-04-06 10:45:48 +090074 line = bytes.TrimRight(line, "\n")
Shinichiro Hamajie1841582015-03-30 17:20:33 +090075
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +090076 return line
77}
78
79func removeComment(line []byte) []byte {
80 var parenStack []byte
81 for i, ch := range line {
82 switch ch {
83 case '(', '{':
84 parenStack = append(parenStack, ch)
85 case ')', '}':
86 if len(parenStack) > 0 {
87 parenStack = parenStack[:len(parenStack)-1]
88 }
89 case '#':
90 if len(parenStack) == 0 {
91 return line[:i]
92 }
93 }
94 }
95 return line
96}
97
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +090098func (p *parser) processMakefileLine(line []byte) []byte {
Shinichiro Hamajie1841582015-03-30 17:20:33 +090099 // TODO: Handle \\ at the end of the line?
100 for len(line) > 0 && line[len(line)-1] == '\\' {
101 line = line[:len(line)-1]
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900102 lineno := p.lineno
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900103 nline := p.readLine()
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +0900104 p.lineno = lineno
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900105 line = append(line, nline...)
106 }
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900107 return removeComment(line)
108}
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900109
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900110func (p *parser) processRecipeLine(line []byte) []byte {
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900111 // TODO: Handle \\ at the end of the line?
112 for len(line) > 0 && line[len(line)-1] == '\\' {
113 line = append(line, '\n')
114 lineno := p.lineno
115 nline := p.readLine()
116 p.lineno = lineno
117 line = append(line, nline...)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900118 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900119 return line
120}
121
122func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900123 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900124 panic("unreadLine twice!")
125 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900126 p.unBuf = line
127 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900128}
129
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900130func (p *parser) parseAssign(line []byte, sep, esep int) AST {
Fumitoshi Ukai8773e5e2015-04-01 11:23:18 +0900131 Log("parseAssign %q op:%q", line, line[sep:esep])
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900132 ast := &AssignAST{
133 lhs: string(bytes.TrimSpace(line[:sep])),
134 rhs: string(bytes.TrimLeft(line[esep:], " \t")),
135 op: string(line[sep:esep]),
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900136 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900137 ast.filename = p.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900138 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900139 return ast
140}
141
Shinichiro Hamajide829712015-03-31 18:26:56 +0900142func (p *parser) parseMaybeRule(line string) AST {
143 if len(strings.TrimSpace(line)) == 0 {
144 return nil
145 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900146
Shinichiro Hamaji19f4bf62015-04-02 04:55:57 +0900147 ast := &MaybeRuleAST{}
148 if i := strings.IndexByte(line, ';'); i >= 0 {
149 ast.expr = line[:i]
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900150 ast.cmd = strings.TrimSpace(line[i+1:])
Shinichiro Hamaji19f4bf62015-04-02 04:55:57 +0900151 } else {
152 ast.expr = line
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900153 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900154 ast.filename = p.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900155 ast.lineno = p.lineno
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900156 /*
Shinichiro Hamaji7c4e3252015-03-30 23:04:25 +0900157 ast.cmdLineno = p.elineno + 1
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900158 for {
Shinichiro Hamaji52e83aa2015-04-06 17:20:28 +0900159 line := p.readRecipeLine()
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900160 if len(line) == 0 {
161 break
162 } else if line[0] == '\t' {
Shinichiro Hamaji79abd182015-04-03 11:07:38 +0900163 ast.cmds = append(ast.cmds, string(bytes.TrimLeft(line, " \t")))
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900164 } else {
165 p.unreadLine(line)
166 break
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900167 }
168 }
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900169 */
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900170 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900171}
172
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900173func (p *parser) parseInclude(line string, oplen int) AST {
174 ast := &IncludeAST{
175 expr: line[oplen+1:],
176 op: line[:oplen],
177 }
178 ast.filename = p.filename
179 ast.lineno = p.lineno
180 return ast
181}
182
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900183func (p *parser) parseIfdef(line string, oplen int) AST {
184 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900185 op: line[:oplen],
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900186 lhs: strings.TrimSpace(line[oplen+1:]),
187 }
188 ast.filename = p.filename
189 ast.lineno = p.lineno
190 p.addStatement(ast)
191 p.ifStack = append(p.ifStack, ifState{ast: ast})
192 p.outStmts = &ast.trueStmts
193 return ast
194}
195
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900196func closeParen(ch byte) (byte, error) {
197 switch ch {
198 case '(':
199 return ')', nil
200 case '{':
201 return '}', nil
202 default:
203 return 0, fmt.Errorf("unexpected paren %c", ch)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900204 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900205}
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900206
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900207// parseExpr parses s as expr.
208// The expr should starts with '(' or '{' and returns strings
209// separeted by ',' before ')' or '}' respectively, and an index for the rest.
210func parseExpr(s string) ([]string, int, error) {
211 if len(s) == 0 {
212 return nil, 0, errors.New("empty expr")
213 }
214 paren, err := closeParen(s[0])
215 if err != nil {
216 return nil, 0, err
217 }
218 parenCnt := make(map[byte]int)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900219 i := 0
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900220 ia := 1
221 var args []string
222Loop:
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900223 for {
224 i++
225 if i == len(s) {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900226 return nil, 0, errors.New("unexpected end of expr")
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900227 }
228 ch := s[i]
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900229 switch ch {
230 case '(', '{':
231 cch, err := closeParen(ch)
232 if err != nil {
233 return nil, 0, err
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900234 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900235 parenCnt[cch]++
236 case ')', '}':
237 parenCnt[ch]--
238 if ch == paren && parenCnt[ch] < 0 {
239 break Loop
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900240 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900241 case ',':
242 if parenCnt[')'] == 0 && parenCnt['}'] == 0 {
243 args = append(args, s[ia:i])
244 ia = i + 1
245 }
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900246 }
247 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900248 args = append(args, s[ia:i])
249 return args, i + 1, nil
250}
251
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900252func parseTwoQuotes(s string) ([]string, bool) {
253 toks := splitSpaces(s)
254 if len(toks) != 2 {
255 return nil, false
256 }
257 var args []string
258 for _, tok := range toks {
259 if len(tok) < 2 {
260 return nil, false
261 }
262 ti := len(tok) - 1
263 if tok[0] != tok[ti] || (tok[0] != '\'' && tok[ti] != '"') {
264 return nil, false
265 }
266 args = append(args, tok[1:ti])
267 }
268 return args, true
269}
270
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900271func parseEq(s string) (string, string, bool) {
272 args, _, err := parseExpr(s)
273 if err != nil {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900274 args, ok := parseTwoQuotes(s)
275 if ok {
276 return args[0], args[1], true
277 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900278 return "", "", false
279 }
280 if len(args) != 2 {
281 return "", "", false
282 }
283 // TODO: check rest?
Shinichiro Hamajie56f2222015-04-07 05:09:00 +0900284 return args[0], strings.TrimLeft(args[1], " \t"), true
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900285}
286
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900287func (p *parser) parseIfeq(line string, oplen int) AST {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900288 lhs, rhs, ok := parseEq(strings.TrimSpace(line[oplen+1:]))
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900289 if !ok {
290 Error(p.filename, p.lineno, `*** invalid syntax in conditional.`)
291 }
292
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900293 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900294 op: line[:oplen],
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900295 lhs: lhs,
296 rhs: rhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900297 }
298 ast.filename = p.filename
299 ast.lineno = p.lineno
300 p.addStatement(ast)
301 p.ifStack = append(p.ifStack, ifState{ast: ast})
302 p.outStmts = &ast.trueStmts
303 return ast
304}
305
306func (p *parser) checkIfStack(curKeyword string) {
307 if len(p.ifStack) == 0 {
308 Error(p.filename, p.lineno, `*** extraneous %q.`, curKeyword)
309 }
310}
311
312func (p *parser) parseElse(line string) {
313 p.checkIfStack("else")
314 state := &p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900315 if state.inElse {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900316 Error(p.filename, p.lineno, `*** only one "else" per conditional.`)
317 }
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900318 state.inElse = true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900319 p.outStmts = &state.ast.falseStmts
320}
321
322func (p *parser) parseEndif(line string) {
323 p.checkIfStack("endif")
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900324 p.ifStack = p.ifStack[0 : len(p.ifStack)-1]
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900325 if len(p.ifStack) == 0 {
326 p.outStmts = &p.mk.stmts
327 } else {
328 state := p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900329 if state.inElse {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900330 p.outStmts = &state.ast.falseStmts
331 } else {
332 p.outStmts = &state.ast.trueStmts
333 }
334 }
335}
336
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900337var directives = map[string]func(*parser, string){
338 "include ": includeDirective,
339 "-include ": sincludeDirective,
340 "sinclude": sincludeDirective,
341 "ifdef ": ifdefDirective,
342 "ifndef ": ifndefDirective,
343 "ifeq ": ifeqDirective,
344 "ifneq ": ifneqDirective,
345 "else": elseDirective,
346 "endif": endifDirective,
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900347 "define ": defineDirective,
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900348}
349
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900350func (p *parser) parseKeywords(line string) bool {
Shinichiro Hamajie3a94632015-03-31 01:12:52 +0900351 stripped := strings.TrimLeft(line, " \t")
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900352 for prefix, f := range directives {
353 if strings.HasPrefix(stripped, prefix) {
354 f(p, stripped)
355 return true
356 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900357 }
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900358 return false
359}
360
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900361func includeDirective(p *parser, line string) {
362 p.addStatement(p.parseInclude(line, len("include")))
363}
364
365func sincludeDirective(p *parser, line string) {
366 p.addStatement(p.parseInclude(line, len("-include")))
367}
368
369func ifdefDirective(p *parser, line string) {
370 p.parseIfdef(line, len("ifdef"))
371}
372
373func ifndefDirective(p *parser, line string) {
374 p.parseIfdef(line, len("ifndef"))
375}
376
377func ifeqDirective(p *parser, line string) {
378 p.parseIfeq(line, len("ifeq"))
379}
380
381func ifneqDirective(p *parser, line string) {
382 p.parseIfeq(line, len("ifneq"))
383}
384
385func elseDirective(p *parser, line string) {
386 p.parseElse(line)
387}
388
389func endifDirective(p *parser, line string) {
390 p.parseEndif(line)
391}
392
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900393func defineDirective(p *parser, line string) {
394 p.inDef = []string{strings.TrimLeft(line[len("define "):], " \t")}
395}
396
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900397func (p *parser) parse() (mk Makefile, err error) {
398 defer func() {
399 if r := recover(); r != nil {
400 err = fmt.Errorf("panic: %v", r)
401 }
402 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900403 for !p.done {
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900404 line := p.readLine()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900405
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900406 if len(p.inDef) > 0 {
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900407 line = p.processMakefileLine(line)
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900408 if strings.TrimLeft(string(line), " ") == "endef" {
409 Log("multilineAssign %q", p.inDef)
410 ast := &AssignAST{
411 lhs: p.inDef[0],
412 rhs: strings.Join(p.inDef[1:], "\n"),
413 op: "=",
414 }
415 ast.filename = p.filename
416 ast.lineno = p.lineno - len(p.inDef)
417 p.addStatement(ast)
418 p.inDef = nil
419 continue
420 }
421 p.inDef = append(p.inDef, string(line))
422 continue
423 }
424
Shinichiro Hamaji0b93c862015-04-07 06:15:15 +0900425 if len(line) == 0 {
426 continue
427 }
428 if line[0] == '\t' {
429 ast := &CommandAST{cmd: string(p.processRecipeLine(line[1:]))}
430 ast.filename = p.filename
431 ast.lineno = p.lineno
432 p.addStatement(ast)
433 continue
434 }
435
436 line = p.processMakefileLine(line)
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900437 if p.parseKeywords(string(line)) {
438 continue
439 }
440
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900441 var ast AST
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900442 var parenStack []byte
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900443 for i, ch := range line {
444 switch ch {
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900445 case '(', '{':
446 parenStack = append(parenStack, ch)
447 case ')', '}':
448 if len(parenStack) == 0 {
449 Warn(p.filename, p.lineno, "Unmatched parens: %s", line)
450 } else {
451 parenStack = parenStack[:len(parenStack)-1]
452 }
453 }
454 if len(parenStack) > 0 {
455 continue
456 }
457
458 switch ch {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900459 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900460 if i+1 < len(line) && line[i+1] == '=' {
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900461 ast = p.parseAssign(line, i, i+2)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900462 } else {
Shinichiro Hamajide829712015-03-31 18:26:56 +0900463 ast = p.parseMaybeRule(string(line))
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900464 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900465 case '=':
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900466 ast = p.parseAssign(line, i, i+1)
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900467 case '?', '+':
468 if i+1 < len(line) && line[i+1] == '=' {
469 ast = p.parseAssign(line, i, i+2)
470 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900471 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900472 if ast != nil {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900473 p.addStatement(ast)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900474 break
475 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900476 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900477 if ast == nil {
478 ast = p.parseMaybeRule(string(line))
479 if ast != nil {
480 p.addStatement(ast)
481 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900482 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900483 }
484 return p.mk, nil
485}
486
487func ParseMakefile(filename string) (Makefile, error) {
488 f, err := os.Open(filename)
489 if err != nil {
490 return Makefile{}, err
491 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900492 defer f.Close()
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900493 parser := newParser(f, filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900494 return parser.parse()
495}
496
497func ParseDefaultMakefile() (Makefile, error) {
498 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
499 for _, filename := range candidates {
500 if exists(filename) {
501 return ParseMakefile(filename)
502 }
503 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900504 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900505}
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900506
Shinichiro Hamaji29ffc972015-04-06 16:13:27 +0900507func ParseMakefileString(s string, name string, lineno int) (Makefile, error) {
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900508 rd := strings.NewReader(s)
509 parser := newParser(rd, name)
Shinichiro Hamaji29ffc972015-04-06 16:13:27 +0900510 parser.lineno = lineno - 1
511 parser.elineno = lineno
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900512 return parser.parse()
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900513}