fix target_specific_var
diff --git a/eval.go b/eval.go
index beef685..a31235a 100644
--- a/eval.go
+++ b/eval.go
@@ -15,7 +15,6 @@
outVars *VarTab
outRules []*Rule
vars *VarTab
- curRule *Rule
funcs map[string]Func
@@ -141,13 +140,18 @@
}
func (ev *Evaluator) evalAssign(ast *AssignAST) {
+ lhs, rhs := ev.evalAssignAST(ast)
+ Log("ASSIGN: %s=%q (flavor:%q)", lhs, rhs, rhs.Flavor())
+ ev.outVars.Assign(lhs, rhs)
+}
+
+func (ev *Evaluator) evalAssignAST(ast *AssignAST) (string, Var) {
ev.filename = ast.filename
ev.lineno = ast.lineno
lhs := ev.evalExpr(ast.lhs)
rhs := ast.evalRHS(ev, lhs)
- Log("ASSIGN: %s=%q (flavor:%q)", lhs, rhs, rhs.Flavor())
- ev.outVars.Assign(lhs, rhs)
+ return lhs, rhs
}
func (ev *Evaluator) evalMaybeRule(ast *MaybeRuleAST) {
@@ -164,27 +168,35 @@
return
}
- ev.curRule = &Rule{
+ rule := &Rule{
filename: ast.filename,
lineno: ast.lineno,
cmdLineno: ast.cmdLineno,
}
- if err := ev.curRule.parse(line); err != "" {
- Error(ast.filename, ast.lineno, err)
+ assign, err := rule.parse(line)
+ if err != nil {
+ Error(ast.filename, ast.lineno, err.Error())
}
- Log("rule %q => outputs:%q, inputs:%q", line, ev.curRule.outputs, ev.curRule.inputs)
+ Log("rule %q => outputs:%q, inputs:%q", line, rule.outputs, rule.inputs)
// It seems rules with no outputs are siliently ignored.
- if len(ev.curRule.outputs) == 0 && len(ev.curRule.outputPatterns) == 0 {
- ev.curRule = nil
+ if len(rule.outputs) == 0 && len(rule.outputPatterns) == 0 {
return
}
// TODO: Pretty print.
//Log("RULE: %s=%s (%d commands)", lhs, rhs, len(cmds))
- ev.curRule.cmds = ast.cmds
- ev.outRules = append(ev.outRules, ev.curRule)
- ev.curRule = nil
+ if assign != nil {
+ if len(ast.cmds) > 0 {
+ Error(ast.filename, ast.lineno, "*** commands commence before first target. Stop.")
+ }
+ rule.vars = NewVarTab(nil)
+ lhs, rhs := ev.evalAssignAST(assign)
+ rule.vars.Assign(lhs, rhs)
+ } else {
+ rule.cmds = ast.cmds
+ }
+ ev.outRules = append(ev.outRules, rule)
}
func (ev *Evaluator) LookupVar(name string) Var {
diff --git a/exec.go b/exec.go
index 15c1e5f..4fed885 100644
--- a/exec.go
+++ b/exec.go
@@ -152,6 +152,12 @@
}
return outputTs, fmt.Errorf("no rule to make target %q", output)
}
+ if rule.vars != nil {
+ vars = NewVarTab(vars)
+ for k, v := range rule.vars.m {
+ vars.Assign(k, v)
+ }
+ }
latest := int64(-1)
var actualInputs []string
@@ -260,6 +266,20 @@
isSuffixRule := ex.populateSuffixRule(rule, output)
if oldRule, present := ex.rules[output]; present {
+ if oldRule.vars != nil || rule.vars != nil {
+ oldRule.isDoubleColon = rule.isDoubleColon
+ switch {
+ case rule.vars == nil && oldRule.vars != nil:
+ rule.vars = oldRule.vars
+ case rule.vars != nil && oldRule.vars == nil:
+ case rule.vars != nil && oldRule.vars != nil:
+ // parent would be the same vars?
+ for k, v := range rule.vars.m {
+ oldRule.vars.m[k] = v
+ }
+ rule.vars = oldRule.vars
+ }
+ }
if oldRule.isDoubleColon != rule.isDoubleColon {
Error(rule.filename, rule.lineno, "*** target file %q has both : and :: entries.", output)
}
diff --git a/rule_parser.go b/rule_parser.go
index c7ee1bd..729f2d3 100644
--- a/rule_parser.go
+++ b/rule_parser.go
@@ -1,6 +1,7 @@
package main
import (
+ "errors"
"strings"
)
@@ -11,6 +12,7 @@
outputPatterns []string
isDoubleColon bool
isSuffixRule bool
+ vars *VarTab
cmds []string
filename string
lineno int
@@ -37,10 +39,38 @@
}
}
-func (r *Rule) parse(line string) string {
+func (r *Rule) parseVar(s string) *AssignAST {
+ eq := strings.IndexByte(s, '=')
+ if eq <= 0 {
+ return nil
+ }
+ assign := &AssignAST{
+ rhs: strings.TrimLeft(s[eq+1:], " \t"),
+ }
+ assign.filename = r.filename
+ assign.lineno = r.lineno
+ // TODO(ukai): support override, export.
+ switch s[eq-1 : eq] {
+ case ":=":
+ assign.lhs = strings.TrimSpace(s[:eq-1])
+ assign.op = ":="
+ case "+=":
+ assign.lhs = strings.TrimSpace(s[:eq-1])
+ assign.op = "+="
+ case "?=":
+ assign.lhs = strings.TrimSpace(s[:eq-1])
+ assign.op = "?="
+ default:
+ assign.lhs = strings.TrimSpace(s[:eq])
+ assign.op = "="
+ }
+ return assign
+}
+
+func (r *Rule) parse(line string) (*AssignAST, error) {
index := strings.IndexByte(line, ':')
if index < 0 {
- return "*** missing separator."
+ return nil, errors.New("*** missing separator.")
}
first := line[:index]
@@ -48,7 +78,7 @@
isFirstPattern := isPatternRule(first)
if isFirstPattern {
if len(outputs) > 1 {
- return "*** mixed implicit and normal rules: deprecated syntax"
+ return nil, errors.New("*** mixed implicit and normal rules: deprecated syntax")
}
r.outputPatterns = outputs
} else {
@@ -62,15 +92,18 @@
}
rest := line[index:]
+ if assign := r.parseVar(rest); assign != nil {
+ return assign, nil
+ }
index = strings.IndexByte(rest, ':')
if index < 0 {
r.parseInputs(rest)
- return ""
+ return nil, nil
}
// %.x: %.y: %.z
if isFirstPattern {
- return "*** mixed implicit and normal rules: deprecated syntax"
+ return nil, errors.New("*** mixed implicit and normal rules: deprecated syntax")
}
second := rest[:index]
@@ -79,15 +112,15 @@
r.outputs = outputs
r.outputPatterns = splitSpaces(second)
if len(r.outputPatterns) == 0 {
- return "*** missing target pattern."
+ return nil, errors.New("*** missing target pattern.")
}
if len(r.outputPatterns) > 1 {
- return "*** multiple target patterns."
+ return nil, errors.New("*** multiple target patterns.")
}
if !isPatternRule(r.outputPatterns[0]) {
- return "*** target pattern contains no '%'."
+ return nil, errors.New("*** target pattern contains no '%'.")
}
r.parseInputs(third)
- return ""
+ return nil, nil
}
diff --git a/rule_parser_test.go b/rule_parser_test.go
index f44c589..dc66d0b 100644
--- a/rule_parser_test.go
+++ b/rule_parser_test.go
@@ -7,29 +7,30 @@
func TestRuleParser(t *testing.T) {
for _, tc := range []struct {
- in string
- want Rule
- err string
- } {
+ in string
+ want Rule
+ assign *AssignAST
+ err string
+ }{
{
- in: "foo: bar",
+ in: "foo: bar",
want: Rule{
outputs: []string{"foo"},
inputs: []string{"bar"},
},
},
{
- in: "foo: bar baz",
+ in: "foo: bar baz",
want: Rule{
outputs: []string{"foo"},
inputs: []string{"bar", "baz"},
},
},
{
- in: "foo:: bar",
+ in: "foo:: bar",
want: Rule{
- outputs: []string{"foo"},
- inputs: []string{"bar"},
+ outputs: []string{"foo"},
+ inputs: []string{"bar"},
isDoubleColon: true,
},
},
@@ -38,7 +39,7 @@
err: "*** missing separator.",
},
{
- in: "%.o: %.c",
+ in: "%.o: %.c",
want: Rule{
outputPatterns: []string{"%.o"},
inputs: []string{"%.c"},
@@ -49,7 +50,7 @@
err: "*** mixed implicit and normal rules: deprecated syntax",
},
{
- in: "foo.o: %.o: %.c %.h",
+ in: "foo.o: %.o: %.c %.h",
want: Rule{
outputs: []string{"foo.o"},
outputPatterns: []string{"%.o"},
@@ -73,13 +74,24 @@
err: "*** target pattern contains no '%'.",
},
{
- in: "foo: bar | baz",
+ in: "foo: bar | baz",
want: Rule{
outputs: []string{"foo"},
inputs: []string{"bar"},
orderOnlyInputs: []string{"baz"},
},
},
+ {
+ in: "foo: CFLAGS = -g",
+ want: Rule{
+ outputs: []string{"foo"},
+ },
+ assign: &AssignAST{
+ lhs: "CFLAGS",
+ rhs: "-g",
+ op: "=",
+ },
+ },
/* TODO
{
in: "foo.o: %.c: %.c",
@@ -88,12 +100,36 @@
*/
} {
got := &Rule{}
- err := got.parse(tc.in)
- if err != tc.err {
- t.Errorf(`r.parse(%q)=%s, want %s`, tc.in, err, tc.err)
+ assign, err := got.parse(tc.in)
+ if tc.err != "" {
+ if err == nil {
+ t.Errorf(`r.parse(%q)=_, <nil>, want _, %q`, tc.in, tc.err)
+ continue
+ }
+ if got, want := err.Error(), tc.err; got != want {
+ t.Errorf(`r.parse(%q)=_, %s, want %s`, tc.in, got, want)
+ }
+ continue
}
- if err == "" && !reflect.DeepEqual(*got, tc.want) {
+ if err != nil {
+ t.Errorf(`r.parse(%q)=_, %v; want nil error`, tc.in, err)
+ continue
+ }
+ if !reflect.DeepEqual(*got, tc.want) {
t.Errorf(`r.parse(%q); r=%q, want %q`, tc.in, *got, tc.want)
}
+ if tc.assign != nil {
+ if assign == nil {
+ t.Errorf(`r.parse(%q)=<nil>; want=%v`, tc.in, tc.assign)
+ continue
+ }
+ if got, want := assign, tc.assign; !reflect.DeepEqual(got, want) {
+ t.Errorf(`r.parse(%q)=%v; want=%v`, got, want)
+ }
+ continue
+ }
+ if assign != nil {
+ t.Errorf(`r.parse(%q)=%v; want=<nil>`, tc.in, assign)
+ }
}
}
diff --git a/test/target_specific_var.mk b/test/target_specific_var.mk
index 1696344..54b3fd0 100644
--- a/test/target_specific_var.mk
+++ b/test/target_specific_var.mk
@@ -1,4 +1,3 @@
-# TODO
# https://www.gnu.org/software/make/manual/html_node/Target_002dspecific.html
CFLAGS = -O