blob: 6a0aeb9f100529fb090287132d173ff542040383 [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"
7 "io"
8 "os"
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09009)
10
11type Makefile struct {
12 stmts []AST
13}
14
15type parser struct {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090016 rd *bufio.Reader
17 mk Makefile
18 lineno int
19 elineno int // lineno == elineno unless there is trailing '\'.
20 unBuf []byte
21 hasUnBuf bool
22 done bool
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090023}
24
25func exists(filename string) bool {
26 f, err := os.Open(filename)
27 if err != nil {
28 return false
29 }
30 f.Close()
31 return true
32}
33
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090034func newParser(rd io.Reader) *parser {
35 return &parser{
36 rd: bufio.NewReader(rd),
37 }
38}
39
Shinichiro Hamajie1841582015-03-30 17:20:33 +090040func (p *parser) readLine() []byte {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090041 if p.hasUnBuf {
42 p.hasUnBuf = false
43 return p.unBuf
Shinichiro Hamajie1841582015-03-30 17:20:33 +090044 }
45
46 p.lineno = p.elineno
47 line, err := p.rd.ReadBytes('\n')
48 p.lineno++
49 if err == io.EOF {
50 p.done = true
51 } else if err != nil {
52 panic(err)
53 }
54
55 if len(line) > 0 {
56 line = line[0 : len(line)-1]
57 }
58
59 // TODO: Handle \\ at the end of the line?
60 for len(line) > 0 && line[len(line)-1] == '\\' {
61 line = line[:len(line)-1]
62 nline := p.readLine()
63 p.elineno++
64 line = append(line, nline...)
65 }
66
67 index := bytes.IndexByte(line, '#')
68 if index >= 0 {
69 line = line[:index]
70 }
71
72 return line
73}
74
75func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090076 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +090077 panic("unreadLine twice!")
78 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090079 p.unBuf = line
80 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +090081}
82
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +090083func (p *parser) parseAssign(line []byte, sep int, typ int) AST {
84 Log("parseAssign %s %d", line, sep)
85 esep := sep + 1
86 if typ != ASSIGN_RECURSIVE {
87 esep++
88 }
89 lhs := string(bytes.TrimSpace(line[:sep]))
90 rhs := string(bytes.TrimLeft(line[esep:], " \t"))
91 ast := &AssignAST{lhs: lhs, rhs: rhs, assign_type: typ}
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090092 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +090093 return ast
94}
95
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +090096func (p *parser) parseRule(line []byte, sep int) AST {
97 lhs := string(bytes.TrimSpace(line[:sep]))
98 rhs := string(bytes.TrimSpace(line[sep+1:]))
99 ast := &RuleAST{
100 lhs: lhs,
101 rhs: rhs,
102 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900103 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900104 for {
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900105 line := p.readLine()
106 if len(line) == 0 {
107 break
108 } else if line[0] == '\t' {
109 ast.cmds = append(ast.cmds, string(bytes.TrimSpace(line)))
110 } else {
111 p.unreadLine(line)
112 break
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900113 }
114 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900115 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900116}
117
118func (p *parser) parse() (Makefile, error) {
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900119 for !p.done {
120 line := p.readLine()
121
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900122 var ast AST
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900123 for i, ch := range line {
124 switch ch {
125 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900126 if i+1 < len(line) && line[i+1] == '=' {
127 ast = p.parseAssign(line, i, ASSIGN_SIMPLE)
128 } else {
129 ast = p.parseRule(line, i)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900130 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900131 case '=':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900132 ast = p.parseAssign(line, i, ASSIGN_RECURSIVE)
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900133 case '?':
134 panic("TODO")
135 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900136 if ast != nil {
137 p.mk.stmts = append(p.mk.stmts, ast)
138 break
139 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900140 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900141 }
142 return p.mk, nil
143}
144
145func ParseMakefile(filename string) (Makefile, error) {
146 f, err := os.Open(filename)
147 if err != nil {
148 return Makefile{}, err
149 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900150 defer f.Close()
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900151 parser := newParser(f)
152 return parser.parse()
153}
154
155func ParseDefaultMakefile() (Makefile, error) {
156 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
157 for _, filename := range candidates {
158 if exists(filename) {
159 return ParseMakefile(filename)
160 }
161 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900162 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900163}