blob: 43666f82ea475f77f1598e50dbed6bb917ddb788 [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"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090010)
11
12type Makefile struct {
13 stmts []AST
14}
15
16type parser struct {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090017 rd *bufio.Reader
18 mk Makefile
19 lineno int
20 elineno int // lineno == elineno unless there is trailing '\'.
21 unBuf []byte
22 hasUnBuf bool
23 done bool
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090024}
25
26func exists(filename string) bool {
27 f, err := os.Open(filename)
28 if err != nil {
29 return false
30 }
31 f.Close()
32 return true
33}
34
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090035func newParser(rd io.Reader) *parser {
36 return &parser{
37 rd: bufio.NewReader(rd),
38 }
39}
40
Shinichiro Hamajie1841582015-03-30 17:20:33 +090041func (p *parser) readLine() []byte {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090042 if p.hasUnBuf {
43 p.hasUnBuf = false
44 return p.unBuf
Shinichiro Hamajie1841582015-03-30 17:20:33 +090045 }
46
47 p.lineno = p.elineno
48 line, err := p.rd.ReadBytes('\n')
49 p.lineno++
50 if err == io.EOF {
51 p.done = true
52 } else if err != nil {
53 panic(err)
54 }
55
56 if len(line) > 0 {
57 line = line[0 : len(line)-1]
58 }
59
60 // TODO: Handle \\ at the end of the line?
61 for len(line) > 0 && line[len(line)-1] == '\\' {
62 line = line[:len(line)-1]
63 nline := p.readLine()
64 p.elineno++
65 line = append(line, nline...)
66 }
67
68 index := bytes.IndexByte(line, '#')
69 if index >= 0 {
70 line = line[:index]
71 }
72
73 return line
74}
75
76func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090077 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +090078 panic("unreadLine twice!")
79 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090080 p.unBuf = line
81 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +090082}
83
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +090084func (p *parser) parseAssign(line []byte, sep int, typ int) AST {
85 Log("parseAssign %s %d", line, sep)
86 esep := sep + 1
87 if typ != ASSIGN_RECURSIVE {
88 esep++
89 }
90 lhs := string(bytes.TrimSpace(line[:sep]))
91 rhs := string(bytes.TrimLeft(line[esep:], " \t"))
92 ast := &AssignAST{lhs: lhs, rhs: rhs, assign_type: typ}
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090093 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090094 return ast
95}
96
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +090097func (p *parser) parseRule(line []byte, sep int) AST {
98 lhs := string(bytes.TrimSpace(line[:sep]))
99 rhs := string(bytes.TrimSpace(line[sep+1:]))
100 ast := &RuleAST{
101 lhs: lhs,
102 rhs: rhs,
103 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900104 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900105 for {
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900106 line := p.readLine()
107 if len(line) == 0 {
108 break
109 } else if line[0] == '\t' {
110 ast.cmds = append(ast.cmds, string(bytes.TrimSpace(line)))
111 } else {
112 p.unreadLine(line)
113 break
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900114 }
115 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900116 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900117}
118
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900119func (p *parser) parse() (mk Makefile, err error) {
120 defer func() {
121 if r := recover(); r != nil {
122 err = fmt.Errorf("panic: %v", r)
123 }
124 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900125 for !p.done {
126 line := p.readLine()
127
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900128 var ast AST
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900129 for i, ch := range line {
130 switch ch {
131 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900132 if i+1 < len(line) && line[i+1] == '=' {
133 ast = p.parseAssign(line, i, ASSIGN_SIMPLE)
134 } else {
135 ast = p.parseRule(line, i)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900136 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900137 case '=':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900138 ast = p.parseAssign(line, i, ASSIGN_RECURSIVE)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900139 case '?':
140 panic("TODO")
141 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900142 if ast != nil {
143 p.mk.stmts = append(p.mk.stmts, ast)
144 break
145 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900146 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900147 }
148 return p.mk, nil
149}
150
151func ParseMakefile(filename string) (Makefile, error) {
152 f, err := os.Open(filename)
153 if err != nil {
154 return Makefile{}, err
155 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900156 defer f.Close()
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900157 parser := newParser(f)
158 return parser.parse()
159}
160
161func ParseDefaultMakefile() (Makefile, error) {
162 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
163 for _, filename := range candidates {
164 if exists(filename) {
165 return ParseMakefile(filename)
166 }
167 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900168 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900169}