Parse complex rules
diff --git a/eval.go b/eval.go
index 33fb00b..75009a8 100644
--- a/eval.go
+++ b/eval.go
@@ -174,7 +174,9 @@
lineno: ast.lineno,
cmdLineno: ast.cmdLineno,
}
- ev.curRule.parse(line)
+ if err := ev.curRule.parse(line); err != "" {
+ Error(ast.filename, ast.lineno, err)
+ }
// It seems rules with no outputs are siliently ignored.
if len(ev.curRule.outputs) == 0 {
ev.curRule = nil
diff --git a/parser_test.go b/parser_test.go
index 140ae1e..bdc94fb 100644
--- a/parser_test.go
+++ b/parser_test.go
@@ -47,8 +47,7 @@
}
if got, want := args, tc.args; !reflect.DeepEqual(got, want) {
t.Errorf(`parseExpr(%q)=%q, _, _; want %q, _, _`, tc.in, got, want)
- }
- if got, want := tc.in[rest:], tc.rest; got != want {
+ } else if got, want := tc.in[rest:], tc.rest; got != want {
t.Errorf(`parseExpr(%q)=_, %q, _; want _, %q, _`, tc.in, got, want)
}
}
diff --git a/rule_parser.go b/rule_parser.go
index ce74878..7ce218e 100644
--- a/rule_parser.go
+++ b/rule_parser.go
@@ -5,20 +5,71 @@
)
type Rule struct {
- outputs []string
- inputs []string
- cmds []string
- filename string
- lineno int
- cmdLineno int
+ outputs []string
+ inputs []string
+ outputPatterns []string
+ isDoubleColon bool
+ cmds []string
+ filename string
+ lineno int
+ cmdLineno int
}
-func (r *Rule) parse(line string) {
- colonIndex := strings.IndexByte(line, ':')
- if colonIndex < 0 {
- Error(r.filename, r.lineno, "*** missing separator.")
+func isPatternRule(s string) bool {
+ return strings.IndexByte(s, '%') >= 0
+}
+
+func (r *Rule) parse(line string) string {
+ index := strings.IndexByte(line, ':')
+ if index < 0 {
+ return "*** missing separator."
}
- r.outputs = splitSpaces(line[:colonIndex])
- r.inputs = splitSpaces(line[colonIndex+1:])
+ first := line[:index]
+ outputs := splitSpaces(first)
+ isFirstPattern := isPatternRule(first)
+ if isFirstPattern {
+ if len(outputs) > 1 {
+ return "*** mixed implicit and normal rules: deprecated syntax"
+ }
+ r.outputPatterns = outputs
+ } else {
+ r.outputs = outputs
+ }
+
+ index++
+ if index < len(line) && line[index] == ':' {
+ r.isDoubleColon = true
+ index++
+ }
+
+ rest := line[index:]
+ index = strings.IndexByte(rest, ':')
+ if index < 0 {
+ r.inputs = splitSpaces(rest)
+ return ""
+ }
+
+ // %.x: %.y: %.z
+ if isFirstPattern {
+ return "*** mixed implicit and normal rules: deprecated syntax"
+ }
+
+ second := rest[:index]
+ third := rest[index+1:]
+
+ r.outputs = outputs
+ r.outputPatterns = splitSpaces(second)
+ if len(r.outputPatterns) == 0 {
+ return "*** missing target pattern."
+ }
+ if len(r.outputPatterns) > 1 {
+ return "*** multiple target patterns."
+ }
+ if !isPatternRule(r.outputPatterns[0]) {
+ return "*** target pattern contains no '%'."
+ }
+ r.inputs = splitSpaces(third)
+
+ return ""
}
diff --git a/rule_parser_test.go b/rule_parser_test.go
index 4c76c71..dbc166e 100644
--- a/rule_parser_test.go
+++ b/rule_parser_test.go
@@ -9,6 +9,7 @@
for _, tc := range []struct {
in string
want Rule
+ err string
} {
{
in: "foo: bar",
@@ -24,11 +25,67 @@
inputs: []string{"bar", "baz"},
},
},
+ {
+ in: "foo:: bar",
+ want: Rule{
+ outputs: []string{"foo"},
+ inputs: []string{"bar"},
+ isDoubleColon: true,
+ },
+ },
+ {
+ in: "foo",
+ err: "*** missing separator.",
+ },
+ {
+ in: "%.o: %.c",
+ want: Rule{
+ outputPatterns: []string{"%.o"},
+ inputs: []string{"%.c"},
+ },
+ },
+ {
+ in: "foo %.o: %.c",
+ err: "*** mixed implicit and normal rules: deprecated syntax",
+ },
+ {
+ in: "foo.o: %.o: %.c %.h",
+ want: Rule{
+ outputs: []string{"foo.o"},
+ outputPatterns: []string{"%.o"},
+ inputs: []string{"%.c", "%.h"},
+ },
+ },
+ {
+ in: "%.x: %.y: %.z",
+ err: "*** mixed implicit and normal rules: deprecated syntax",
+ },
+ {
+ in: "foo.o: : %.c",
+ err: "*** missing target pattern.",
+ },
+ {
+ in: "foo.o: %.o %.o: %.c",
+ err: "*** multiple target patterns.",
+ },
+ {
+ in: "foo.o: foo.o: %.c",
+ err: "*** target pattern contains no '%'.",
+ },
+ /* TODO
+ {
+ in: "foo.o: %.c: %.c",
+ err: "*** target 'foo.o' doesn't match the target pattern",
+ },
+ */
} {
got := &Rule{}
- got.parse(tc.in)
- if !reflect.DeepEqual(tc.want, *got) {
- t.Errorf(`r.parse(%q)=%q, want %q`, tc.in, tc.want, *got)
+ err := got.parse(tc.in)
+ if err != tc.err {
+ t.Errorf(`r.parse(%q)=%s, want %s`, tc.in, err, tc.err)
+ }
+ if err == "" && !reflect.DeepEqual(*got, tc.want) {
+ t.Errorf(`r.parse(%q); r=%q, want %q`, tc.in, *got, tc.want)
}
}
}