blob: 2932ae2ece21ba2033ac5d68a387c035def94f58 [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
74 if len(line) > 0 {
75 line = line[0 : len(line)-1]
76 }
77
78 // TODO: Handle \\ at the end of the line?
79 for len(line) > 0 && line[len(line)-1] == '\\' {
80 line = line[:len(line)-1]
81 nline := p.readLine()
82 p.elineno++
83 line = append(line, nline...)
84 }
85
86 index := bytes.IndexByte(line, '#')
87 if index >= 0 {
88 line = line[:index]
89 }
90
91 return line
92}
93
94func (p *parser) unreadLine(line []byte) {
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090095 if p.hasUnBuf {
Shinichiro Hamajie1841582015-03-30 17:20:33 +090096 panic("unreadLine twice!")
97 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +090098 p.unBuf = line
99 p.hasUnBuf = true
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900100}
101
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900102func (p *parser) parseAssign(line []byte, sep, esep int) AST {
Fumitoshi Ukai8773e5e2015-04-01 11:23:18 +0900103 Log("parseAssign %q op:%q", line, line[sep:esep])
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900104 ast := &AssignAST{
105 lhs: string(bytes.TrimSpace(line[:sep])),
106 rhs: string(bytes.TrimLeft(line[esep:], " \t")),
107 op: string(line[sep:esep]),
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900108 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900109 ast.filename = p.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900110 ast.lineno = p.lineno
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900111 return ast
112}
113
Shinichiro Hamajide829712015-03-31 18:26:56 +0900114func (p *parser) parseMaybeRule(line string) AST {
115 if len(strings.TrimSpace(line)) == 0 {
116 return nil
117 }
118 if line[0] == '\t' {
119 Error(p.filename, p.lineno, "*** commands commence before first target.")
120 }
121
Shinichiro Hamaji19f4bf62015-04-02 04:55:57 +0900122 ast := &MaybeRuleAST{}
123 if i := strings.IndexByte(line, ';'); i >= 0 {
124 ast.expr = line[:i]
125 ast.cmds = append(ast.cmds, strings.TrimSpace(line[i+1:]))
126 } else {
127 ast.expr = line
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900128 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900129 ast.filename = p.filename
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900130 ast.lineno = p.lineno
Shinichiro Hamaji7c4e3252015-03-30 23:04:25 +0900131 ast.cmdLineno = p.elineno + 1
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900132 for {
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900133 line := p.readLine()
134 if len(line) == 0 {
135 break
136 } else if line[0] == '\t' {
137 ast.cmds = append(ast.cmds, string(bytes.TrimSpace(line)))
138 } else {
139 p.unreadLine(line)
140 break
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900141 }
142 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900143 return ast
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900144}
145
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900146func (p *parser) parseInclude(line string, oplen int) AST {
147 ast := &IncludeAST{
148 expr: line[oplen+1:],
149 op: line[:oplen],
150 }
151 ast.filename = p.filename
152 ast.lineno = p.lineno
153 return ast
154}
155
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900156func (p *parser) parseIfdef(line string, oplen int) AST {
157 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900158 op: line[:oplen],
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900159 lhs: strings.TrimSpace(line[oplen+1:]),
160 }
161 ast.filename = p.filename
162 ast.lineno = p.lineno
163 p.addStatement(ast)
164 p.ifStack = append(p.ifStack, ifState{ast: ast})
165 p.outStmts = &ast.trueStmts
166 return ast
167}
168
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900169func closeParen(ch byte) (byte, error) {
170 switch ch {
171 case '(':
172 return ')', nil
173 case '{':
174 return '}', nil
175 default:
176 return 0, fmt.Errorf("unexpected paren %c", ch)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900177 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900178}
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900179
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900180// parseExpr parses s as expr.
181// The expr should starts with '(' or '{' and returns strings
182// separeted by ',' before ')' or '}' respectively, and an index for the rest.
183func parseExpr(s string) ([]string, int, error) {
184 if len(s) == 0 {
185 return nil, 0, errors.New("empty expr")
186 }
187 paren, err := closeParen(s[0])
188 if err != nil {
189 return nil, 0, err
190 }
191 parenCnt := make(map[byte]int)
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900192 i := 0
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900193 ia := 1
194 var args []string
195Loop:
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900196 for {
197 i++
198 if i == len(s) {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900199 return nil, 0, errors.New("unexpected end of expr")
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900200 }
201 ch := s[i]
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900202 switch ch {
203 case '(', '{':
204 cch, err := closeParen(ch)
205 if err != nil {
206 return nil, 0, err
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900207 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900208 parenCnt[cch]++
209 case ')', '}':
210 parenCnt[ch]--
211 if ch == paren && parenCnt[ch] < 0 {
212 break Loop
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900213 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900214 case ',':
215 if parenCnt[')'] == 0 && parenCnt['}'] == 0 {
216 args = append(args, s[ia:i])
217 ia = i + 1
218 }
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900219 }
220 }
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900221 args = append(args, s[ia:i])
222 return args, i + 1, nil
223}
224
225func parseEq(s string) (string, string, bool) {
226 args, _, err := parseExpr(s)
227 if err != nil {
228 return "", "", false
229 }
230 if len(args) != 2 {
231 return "", "", false
232 }
233 // TODO: check rest?
234 return args[0], args[1], true
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900235}
236
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900237func (p *parser) parseIfeq(line string, oplen int) AST {
Fumitoshi Ukaie520f262015-03-31 17:27:03 +0900238 lhs, rhs, ok := parseEq(strings.TrimSpace(line[oplen+1:]))
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900239 if !ok {
240 Error(p.filename, p.lineno, `*** invalid syntax in conditional.`)
241 }
242
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900243 ast := &IfAST{
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900244 op: line[:oplen],
Shinichiro Hamajiaf1e8162015-03-31 02:15:37 +0900245 lhs: lhs,
246 rhs: rhs,
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900247 }
248 ast.filename = p.filename
249 ast.lineno = p.lineno
250 p.addStatement(ast)
251 p.ifStack = append(p.ifStack, ifState{ast: ast})
252 p.outStmts = &ast.trueStmts
253 return ast
254}
255
256func (p *parser) checkIfStack(curKeyword string) {
257 if len(p.ifStack) == 0 {
258 Error(p.filename, p.lineno, `*** extraneous %q.`, curKeyword)
259 }
260}
261
262func (p *parser) parseElse(line string) {
263 p.checkIfStack("else")
264 state := &p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900265 if state.inElse {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900266 Error(p.filename, p.lineno, `*** only one "else" per conditional.`)
267 }
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900268 state.inElse = true
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900269 p.outStmts = &state.ast.falseStmts
270}
271
272func (p *parser) parseEndif(line string) {
273 p.checkIfStack("endif")
Shinichiro Hamajiae32b782015-03-31 14:41:19 +0900274 p.ifStack = p.ifStack[0 : len(p.ifStack)-1]
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900275 if len(p.ifStack) == 0 {
276 p.outStmts = &p.mk.stmts
277 } else {
278 state := p.ifStack[len(p.ifStack)-1]
Fumitoshi Ukai0293c7a2015-03-31 16:26:53 +0900279 if state.inElse {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900280 p.outStmts = &state.ast.falseStmts
281 } else {
282 p.outStmts = &state.ast.trueStmts
283 }
284 }
285}
286
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900287var directives = map[string]func(*parser, string){
288 "include ": includeDirective,
289 "-include ": sincludeDirective,
290 "sinclude": sincludeDirective,
291 "ifdef ": ifdefDirective,
292 "ifndef ": ifndefDirective,
293 "ifeq ": ifeqDirective,
294 "ifneq ": ifneqDirective,
295 "else": elseDirective,
296 "endif": endifDirective,
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900297 "define ": defineDirective,
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900298}
299
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900300func (p *parser) parseKeywords(line string) bool {
Shinichiro Hamajie3a94632015-03-31 01:12:52 +0900301 stripped := strings.TrimLeft(line, " \t")
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900302 for prefix, f := range directives {
303 if strings.HasPrefix(stripped, prefix) {
304 f(p, stripped)
305 return true
306 }
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900307 }
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900308 return false
309}
310
Fumitoshi Ukai82096302015-04-01 10:37:47 +0900311func includeDirective(p *parser, line string) {
312 p.addStatement(p.parseInclude(line, len("include")))
313}
314
315func sincludeDirective(p *parser, line string) {
316 p.addStatement(p.parseInclude(line, len("-include")))
317}
318
319func ifdefDirective(p *parser, line string) {
320 p.parseIfdef(line, len("ifdef"))
321}
322
323func ifndefDirective(p *parser, line string) {
324 p.parseIfdef(line, len("ifndef"))
325}
326
327func ifeqDirective(p *parser, line string) {
328 p.parseIfeq(line, len("ifeq"))
329}
330
331func ifneqDirective(p *parser, line string) {
332 p.parseIfeq(line, len("ifneq"))
333}
334
335func elseDirective(p *parser, line string) {
336 p.parseElse(line)
337}
338
339func endifDirective(p *parser, line string) {
340 p.parseEndif(line)
341}
342
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900343func defineDirective(p *parser, line string) {
344 p.inDef = []string{strings.TrimLeft(line[len("define "):], " \t")}
345}
346
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900347func (p *parser) parse() (mk Makefile, err error) {
348 defer func() {
349 if r := recover(); r != nil {
350 err = fmt.Errorf("panic: %v", r)
351 }
352 }()
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900353 for !p.done {
354 line := p.readLine()
355
Fumitoshi Ukai3d54db82015-04-01 11:03:31 +0900356 if len(p.inDef) > 0 {
357 if strings.TrimLeft(string(line), " ") == "endef" {
358 Log("multilineAssign %q", p.inDef)
359 ast := &AssignAST{
360 lhs: p.inDef[0],
361 rhs: strings.Join(p.inDef[1:], "\n"),
362 op: "=",
363 }
364 ast.filename = p.filename
365 ast.lineno = p.lineno - len(p.inDef)
366 p.addStatement(ast)
367 p.inDef = nil
368 continue
369 }
370 p.inDef = append(p.inDef, string(line))
371 continue
372 }
373
Shinichiro Hamajia66a1792015-03-31 18:04:08 +0900374 if p.parseKeywords(string(line)) {
375 continue
376 }
377
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900378 var ast AST
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900379 for i, ch := range line {
380 switch ch {
381 case ':':
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900382 if i+1 < len(line) && line[i+1] == '=' {
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900383 ast = p.parseAssign(line, i, i+2)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900384 } else {
Shinichiro Hamajide829712015-03-31 18:26:56 +0900385 ast = p.parseMaybeRule(string(line))
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900386 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900387 case '=':
Fumitoshi Ukaie1b813c2015-03-30 18:38:21 +0900388 ast = p.parseAssign(line, i, i+1)
Shinichiro Hamaji69b7f652015-03-31 01:01:59 +0900389 case '?', '+':
390 if i+1 < len(line) && line[i+1] == '=' {
391 ast = p.parseAssign(line, i, i+2)
392 }
Shinichiro Hamajie1841582015-03-30 17:20:33 +0900393 }
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900394 if ast != nil {
Shinichiro Hamaji497754d2015-03-31 02:02:11 +0900395 p.addStatement(ast)
Shinichiro Hamaji7f1ede32015-03-30 18:05:22 +0900396 break
397 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900398 }
Shinichiro Hamajide829712015-03-31 18:26:56 +0900399 if ast == nil {
400 ast = p.parseMaybeRule(string(line))
401 if ast != nil {
402 p.addStatement(ast)
403 }
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900404 }
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900405 }
406 return p.mk, nil
407}
408
409func ParseMakefile(filename string) (Makefile, error) {
410 f, err := os.Open(filename)
411 if err != nil {
412 return Makefile{}, err
413 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900414 defer f.Close()
Shinichiro Hamaji685fecf2015-03-30 18:28:12 +0900415 parser := newParser(f, filename)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900416 return parser.parse()
417}
418
419func ParseDefaultMakefile() (Makefile, error) {
420 candidates := []string{"GNUmakefile", "makefile", "Makefile"}
421 for _, filename := range candidates {
422 if exists(filename) {
423 return ParseMakefile(filename)
424 }
425 }
Fumitoshi Ukaicf2b0382015-03-30 17:48:54 +0900426 return Makefile{}, errors.New("no targets specified and no makefile found.")
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900427}
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900428
429func GetBootstrapMakefile() Makefile {
430 bootstrap := `
431CC:=cc
432CXX:=g++
Shinichiro Hamaji1ad69802015-04-02 05:52:02 +0900433MAKE:=kati
Shinichiro Hamajic8cd5232015-04-03 10:21:23 +0900434# Pretend to be GNU make 3.81, for compatibility.
435MAKE_VERSION:=3.81
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900436# TODO: Add more builtin vars.
437
438# http://www.gnu.org/software/make/manual/make.html#Catalogue-of-Rules
439# The document above is actually not correct. See default.c:
440# http://git.savannah.gnu.org/cgit/make.git/tree/default.c?id=4.1
441.c.o:
442 $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<
443.cc.o:
444 $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<
445# TODO: Add more builtin rules.
446`
447 rd := strings.NewReader(bootstrap)
448 parser := newParser(rd, "*bootstrap*")
449 mk, err := parser.parse()
450 if err != nil {
451 panic(err)
452 }
453 return mk
454}