blob: 5571397fb2db50e2522ebdc8988d46cc757966aa [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
76 // TODO: Handle \\ at the end of the line?
77 for len(line) > 0 && line[len(line)-1] == '\\' {
78 line = line[:len(line)-1]
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +090079 lineno := p.lineno
Shinichiro Hamajie1841582015-03-30 17:20:33 +090080 nline := p.readLine()
Shinichiro Hamaji3e4533d2015-04-06 14:03:58 +090081 p.lineno = lineno
Shinichiro Hamajie1841582015-03-30 17:20:33 +090082 line = append(line, nline...)
83 }
84
85 index := bytes.IndexByte(line, '#')
86 if index >= 0 {
87 line = line[:index]
88 }
89
90 return line
91}
92
93func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090094 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +090095 panic("unreadLine twice!")
96 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090097 p.unBuf = line
98 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +090099}
100
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900101func (p *parser) parseAssign(line []byte, sep, esep int) AST {
Fumitoshi Ukai8773e5e2015-04-01 11:23:18 +0900102 Log("parseAssign %q op:%q", line, line[sep:esep])
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900103 ast := &AssignAST{
104 lhs: string(bytes.TrimSpace(line[:sep])),
105 rhs: string(bytes.TrimLeft(line[esep:], " \t")),
106 op: string(line[sep:esep]),
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900107 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900108 ast.filename = p.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900109 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900110 return ast
111}
112
Shinichiro Hamajide829712015-03-31 18:26:56 +0900113func (p *parser) parseMaybeRule(line string) AST {
114 if len(strings.TrimSpace(line)) == 0 {
115 return nil
116 }
117 if line[0] == '\t' {
118 Error(p.filename, p.lineno, "*** commands commence before first target.")
119 }
120
Shinichiro Hamaji19f4bf62015-04-02 04:55:57 +0900121 ast := &MaybeRuleAST{}
122 if i := strings.IndexByte(line, ';'); i >= 0 {
123 ast.expr = line[:i]
124 ast.cmds = append(ast.cmds, strings.TrimSpace(line[i+1:]))
125 } else {
126 ast.expr = line
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900127 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900128 ast.filename = p.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900129 ast.lineno = p.lineno
Shinichiro Hamaji7c4e3252015-03-30 23:04:25 +0900130 ast.cmdLineno = p.elineno + 1
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900131 for {
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900132 line := p.readLine()
133 if len(line) == 0 {
134 break
135 } else if line[0] == '\t' {
Shinichiro Hamaji79abd182015-04-03 11:07:38 +0900136 ast.cmds = append(ast.cmds, string(bytes.TrimLeft(line, " \t")))
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900137 } else {
138 p.unreadLine(line)
139 break
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900140 }
141 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900142 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900143}
144
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900145func (p *parser) parseInclude(line string, oplen int) AST {
146 ast := &IncludeAST{
147 expr: line[oplen+1:],
148 op: line[:oplen],
149 }
150 ast.filename = p.filename
151 ast.lineno = p.lineno
152 return ast
153}
154
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900155func (p *parser) parseIfdef(line string, oplen int) AST {
156 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900157 op: line[:oplen],
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900158 lhs: strings.TrimSpace(line[oplen+1:]),
159 }
160 ast.filename = p.filename
161 ast.lineno = p.lineno
162 p.addStatement(ast)
163 p.ifStack = append(p.ifStack, ifState{ast: ast})
164 p.outStmts = &ast.trueStmts
165 return ast
166}
167
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900168func closeParen(ch byte) (byte, error) {
169 switch ch {
170 case '(':
171 return ')', nil
172 case '{':
173 return '}', nil
174 default:
175 return 0, fmt.Errorf("unexpected paren %c", ch)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900176 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900177}
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900178
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900179// parseExpr parses s as expr.
180// The expr should starts with '(' or '{' and returns strings
181// separeted by ',' before ')' or '}' respectively, and an index for the rest.
182func parseExpr(s string) ([]string, int, error) {
183 if len(s) == 0 {
184 return nil, 0, errors.New("empty expr")
185 }
186 paren, err := closeParen(s[0])
187 if err != nil {
188 return nil, 0, err
189 }
190 parenCnt := make(map[byte]int)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900191 i := 0
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900192 ia := 1
193 var args []string
194Loop:
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900195 for {
196 i++
197 if i == len(s) {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900198 return nil, 0, errors.New("unexpected end of expr")
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900199 }
200 ch := s[i]
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900201 switch ch {
202 case '(', '{':
203 cch, err := closeParen(ch)
204 if err != nil {
205 return nil, 0, err
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900206 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900207 parenCnt[cch]++
208 case ')', '}':
209 parenCnt[ch]--
210 if ch == paren && parenCnt[ch] < 0 {
211 break Loop
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900212 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900213 case ',':
214 if parenCnt[')'] == 0 && parenCnt['}'] == 0 {
215 args = append(args, s[ia:i])
216 ia = i + 1
217 }
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900218 }
219 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900220 args = append(args, s[ia:i])
221 return args, i + 1, nil
222}
223
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900224func parseTwoQuotes(s string) ([]string, bool) {
225 toks := splitSpaces(s)
226 if len(toks) != 2 {
227 return nil, false
228 }
229 var args []string
230 for _, tok := range toks {
231 if len(tok) < 2 {
232 return nil, false
233 }
234 ti := len(tok) - 1
235 if tok[0] != tok[ti] || (tok[0] != '\'' && tok[ti] != '"') {
236 return nil, false
237 }
238 args = append(args, tok[1:ti])
239 }
240 return args, true
241}
242
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900243func parseEq(s string) (string, string, bool) {
244 args, _, err := parseExpr(s)
245 if err != nil {
Shinichiro Hamaji76de43e2015-04-03 10:40:18 +0900246 args, ok := parseTwoQuotes(s)
247 if ok {
248 return args[0], args[1], true
249 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900250 return "", "", false
251 }
252 if len(args) != 2 {
253 return "", "", false
254 }
255 // TODO: check rest?
256 return args[0], args[1], true
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900257}
258
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900259func (p *parser) parseIfeq(line string, oplen int) AST {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900260 lhs, rhs, ok := parseEq(strings.TrimSpace(line[oplen+1:]))
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900261 if !ok {
262 Error(p.filename, p.lineno, `*** invalid syntax in conditional.`)
263 }
264
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900265 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900266 op: line[:oplen],
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900267 lhs: lhs,
268 rhs: rhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900269 }
270 ast.filename = p.filename
271 ast.lineno = p.lineno
272 p.addStatement(ast)
273 p.ifStack = append(p.ifStack, ifState{ast: ast})
274 p.outStmts = &ast.trueStmts
275 return ast
276}
277
278func (p *parser) checkIfStack(curKeyword string) {
279 if len(p.ifStack) == 0 {
280 Error(p.filename, p.lineno, `*** extraneous %q.`, curKeyword)
281 }
282}
283
284func (p *parser) parseElse(line string) {
285 p.checkIfStack("else")
286 state := &p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900287 if state.inElse {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900288 Error(p.filename, p.lineno, `*** only one "else" per conditional.`)
289 }
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900290 state.inElse = true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900291 p.outStmts = &state.ast.falseStmts
292}
293
294func (p *parser) parseEndif(line string) {
295 p.checkIfStack("endif")
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900296 p.ifStack = p.ifStack[0 : len(p.ifStack)-1]
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900297 if len(p.ifStack) == 0 {
298 p.outStmts = &p.mk.stmts
299 } else {
300 state := p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900301 if state.inElse {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900302 p.outStmts = &state.ast.falseStmts
303 } else {
304 p.outStmts = &state.ast.trueStmts
305 }
306 }
307}
308
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900309var directives = map[string]func(*parser, string){
310 "include ": includeDirective,
311 "-include ": sincludeDirective,
312 "sinclude": sincludeDirective,
313 "ifdef ": ifdefDirective,
314 "ifndef ": ifndefDirective,
315 "ifeq ": ifeqDirective,
316 "ifneq ": ifneqDirective,
317 "else": elseDirective,
318 "endif": endifDirective,
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900319 "define ": defineDirective,
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900320}
321
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900322func (p *parser) parseKeywords(line string) bool {
Shinichiro Hamajie3a94632015-03-31 01:12:52 +0900323 stripped := strings.TrimLeft(line, " \t")
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900324 for prefix, f := range directives {
325 if strings.HasPrefix(stripped, prefix) {
326 f(p, stripped)
327 return true
328 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900329 }
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900330 return false
331}
332
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900333func includeDirective(p *parser, line string) {
334 p.addStatement(p.parseInclude(line, len("include")))
335}
336
337func sincludeDirective(p *parser, line string) {
338 p.addStatement(p.parseInclude(line, len("-include")))
339}
340
341func ifdefDirective(p *parser, line string) {
342 p.parseIfdef(line, len("ifdef"))
343}
344
345func ifndefDirective(p *parser, line string) {
346 p.parseIfdef(line, len("ifndef"))
347}
348
349func ifeqDirective(p *parser, line string) {
350 p.parseIfeq(line, len("ifeq"))
351}
352
353func ifneqDirective(p *parser, line string) {
354 p.parseIfeq(line, len("ifneq"))
355}
356
357func elseDirective(p *parser, line string) {
358 p.parseElse(line)
359}
360
361func endifDirective(p *parser, line string) {
362 p.parseEndif(line)
363}
364
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900365func defineDirective(p *parser, line string) {
366 p.inDef = []string{strings.TrimLeft(line[len("define "):], " \t")}
367}
368
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900369func (p *parser) parse() (mk Makefile, err error) {
370 defer func() {
371 if r := recover(); r != nil {
372 err = fmt.Errorf("panic: %v", r)
373 }
374 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900375 for !p.done {
376 line := p.readLine()
377
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900378 if len(p.inDef) > 0 {
379 if strings.TrimLeft(string(line), " ") == "endef" {
380 Log("multilineAssign %q", p.inDef)
381 ast := &AssignAST{
382 lhs: p.inDef[0],
383 rhs: strings.Join(p.inDef[1:], "\n"),
384 op: "=",
385 }
386 ast.filename = p.filename
387 ast.lineno = p.lineno - len(p.inDef)
388 p.addStatement(ast)
389 p.inDef = nil
390 continue
391 }
392 p.inDef = append(p.inDef, string(line))
393 continue
394 }
395
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900396 if p.parseKeywords(string(line)) {
397 continue
398 }
399
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900400 var ast AST
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900401 var parenStack []byte
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900402 for i, ch := range line {
403 switch ch {
Shinichiro Hamaji34e23242015-04-06 15:44:50 +0900404 case '(', '{':
405 parenStack = append(parenStack, ch)
406 case ')', '}':
407 if len(parenStack) == 0 {
408 Warn(p.filename, p.lineno, "Unmatched parens: %s", line)
409 } else {
410 parenStack = parenStack[:len(parenStack)-1]
411 }
412 }
413 if len(parenStack) > 0 {
414 continue
415 }
416
417 switch ch {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900418 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900419 if i+1 < len(line) && line[i+1] == '=' {
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900420 ast = p.parseAssign(line, i, i+2)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900421 } else {
Shinichiro Hamajide829712015-03-31 18:26:56 +0900422 ast = p.parseMaybeRule(string(line))
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900423 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900424 case '=':
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900425 ast = p.parseAssign(line, i, i+1)
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900426 case '?', '+':
427 if i+1 < len(line) && line[i+1] == '=' {
428 ast = p.parseAssign(line, i, i+2)
429 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900430 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900431 if ast != nil {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900432 p.addStatement(ast)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900433 break
434 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900435 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900436 if ast == nil {
437 ast = p.parseMaybeRule(string(line))
438 if ast != nil {
439 p.addStatement(ast)
440 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900441 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900442 }
443 return p.mk, nil
444}
445
446func ParseMakefile(filename string) (Makefile, error) {
447 f, err := os.Open(filename)
448 if err != nil {
449 return Makefile{}, err
450 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900451 defer f.Close()
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900452 parser := newParser(f, filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900453 return parser.parse()
454}
455
456func ParseDefaultMakefile() (Makefile, error) {
457 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
458 for _, filename := range candidates {
459 if exists(filename) {
460 return ParseMakefile(filename)
461 }
462 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900463 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900464}
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900465
Shinichiro Hamaji29ffc972015-04-06 16:13:27 +0900466func ParseMakefileString(s string, name string, lineno int) (Makefile, error) {
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900467 rd := strings.NewReader(s)
468 parser := newParser(rd, name)
Shinichiro Hamaji29ffc972015-04-06 16:13:27 +0900469 parser.lineno = lineno - 1
470 parser.elineno = lineno
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900471 return parser.parse()
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900472}