aboutsummaryrefslogtreecommitdiffstats
path: root/golang/kati/var.go
diff options
context:
space:
mode:
Diffstat (limited to 'golang/kati/var.go')
-rw-r--r--golang/kati/var.go371
1 files changed, 371 insertions, 0 deletions
diff --git a/golang/kati/var.go b/golang/kati/var.go
new file mode 100644
index 0000000..5e7e996
--- /dev/null
+++ b/golang/kati/var.go
@@ -0,0 +1,371 @@
+// Copyright 2015 Google Inc. All rights reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package kati
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+)
+
+// Var is an interface of make variable.
+type Var interface {
+ Value
+ Append(*Evaluator, string) (Var, error)
+ AppendVar(*Evaluator, Value) (Var, error)
+ Flavor() string
+ Origin() string
+ IsDefined() bool
+}
+
+type targetSpecificVar struct {
+ v Var
+ op string
+}
+
+func (v *targetSpecificVar) Append(ev *Evaluator, s string) (Var, error) {
+ nv, err := v.v.Append(ev, s)
+ if err != nil {
+ return nil, err
+ }
+ return &targetSpecificVar{
+ v: nv,
+ op: v.op,
+ }, nil
+}
+func (v *targetSpecificVar) AppendVar(ev *Evaluator, v2 Value) (Var, error) {
+ nv, err := v.v.AppendVar(ev, v2)
+ if err != nil {
+ return nil, err
+ }
+ return &targetSpecificVar{
+ v: nv,
+ op: v.op,
+ }, nil
+}
+func (v *targetSpecificVar) Flavor() string {
+ return v.v.Flavor()
+}
+func (v *targetSpecificVar) Origin() string {
+ return v.v.Origin()
+}
+func (v *targetSpecificVar) IsDefined() bool {
+ return v.v.IsDefined()
+}
+func (v *targetSpecificVar) String() string {
+ // TODO: If we add the info of |op| a test starts
+ // failing. Shouldn't we use this only for debugging?
+ return v.v.String()
+ // return v.v.String() + " (op=" + v.op + ")"
+}
+func (v *targetSpecificVar) Eval(w evalWriter, ev *Evaluator) error {
+ return v.v.Eval(w, ev)
+}
+
+func (v *targetSpecificVar) serialize() serializableVar {
+ return serializableVar{
+ Type: v.op,
+ Children: []serializableVar{v.v.serialize()},
+ }
+}
+
+func (v *targetSpecificVar) dump(d *dumpbuf) {
+ d.Byte(valueTypeTSV)
+ d.Str(v.op)
+ v.v.dump(d)
+}
+
+type simpleVar struct {
+ // space separated. note that each string may contain spaces, so
+ // it is not word list.
+ value []string
+ origin string
+}
+
+func (v *simpleVar) Flavor() string { return "simple" }
+func (v *simpleVar) Origin() string { return v.origin }
+func (v *simpleVar) IsDefined() bool { return true }
+
+func (v *simpleVar) String() string { return strings.Join(v.value, " ") }
+func (v *simpleVar) Eval(w evalWriter, ev *Evaluator) error {
+ space := false
+ for _, v := range v.value {
+ if space {
+ writeByte(w, ' ')
+ }
+ io.WriteString(w, v)
+ space = true
+ }
+ return nil
+}
+func (v *simpleVar) serialize() serializableVar {
+ return serializableVar{
+ Type: "simple",
+ V: v.String(),
+ Origin: v.origin,
+ }
+}
+func (v *simpleVar) dump(d *dumpbuf) {
+ d.Byte(valueTypeSimple)
+ d.Int(len(v.value))
+ for _, v := range v.value {
+ d.Str(v)
+ }
+ d.Str(v.origin)
+}
+
+func (v *simpleVar) Append(ev *Evaluator, s string) (Var, error) {
+ val, _, err := parseExpr([]byte(s), nil, parseOp{})
+ if err != nil {
+ return nil, err
+ }
+ abuf := newEbuf()
+ err = val.Eval(abuf, ev)
+ if err != nil {
+ return nil, err
+ }
+ v.value = append(v.value, abuf.String())
+ abuf.release()
+ return v, nil
+}
+
+func (v *simpleVar) AppendVar(ev *Evaluator, val Value) (Var, error) {
+ abuf := newEbuf()
+ err := val.Eval(abuf, ev)
+ if err != nil {
+ return nil, err
+ }
+ v.value = append(v.value, abuf.String())
+ abuf.release()
+ return v, nil
+}
+
+type automaticVar struct {
+ value []byte
+}
+
+func (v *automaticVar) Flavor() string { return "simple" }
+func (v *automaticVar) Origin() string { return "automatic" }
+func (v *automaticVar) IsDefined() bool { return true }
+
+func (v *automaticVar) String() string { return string(v.value) }
+func (v *automaticVar) Eval(w evalWriter, ev *Evaluator) error {
+ w.Write(v.value)
+ return nil
+}
+func (v *automaticVar) serialize() serializableVar {
+ return serializableVar{Type: ""}
+}
+func (v *automaticVar) dump(d *dumpbuf) {
+ d.err = fmt.Errorf("cannnot dump automatic var:%s", v.value)
+}
+
+func (v *automaticVar) Append(ev *Evaluator, s string) (Var, error) {
+ val, _, err := parseExpr([]byte(s), nil, parseOp{})
+ if err != nil {
+ return nil, err
+ }
+ abuf := newEbuf()
+ err = val.Eval(abuf, ev)
+ if err != nil {
+ return nil, err
+ }
+ value := []string{string(v.value), abuf.String()}
+ abuf.release()
+ return &simpleVar{
+ value: value,
+ origin: "file",
+ }, nil
+}
+
+func (v *automaticVar) AppendVar(ev *Evaluator, val Value) (Var, error) {
+ abuf := newEbuf()
+ err := val.Eval(abuf, ev)
+ if err != nil {
+ return nil, err
+ }
+ value := []string{string(v.value), abuf.String()}
+ abuf.release()
+ return &simpleVar{
+ value: value,
+ origin: "file",
+ }, nil
+}
+
+type recursiveVar struct {
+ expr Value
+ origin string
+}
+
+func (v *recursiveVar) Flavor() string { return "recursive" }
+func (v *recursiveVar) Origin() string { return v.origin }
+func (v *recursiveVar) IsDefined() bool { return true }
+
+func (v *recursiveVar) String() string { return v.expr.String() }
+func (v *recursiveVar) Eval(w evalWriter, ev *Evaluator) error {
+ v.expr.Eval(w, ev)
+ return nil
+}
+func (v *recursiveVar) serialize() serializableVar {
+ return serializableVar{
+ Type: "recursive",
+ Children: []serializableVar{v.expr.serialize()},
+ Origin: v.origin,
+ }
+}
+func (v *recursiveVar) dump(d *dumpbuf) {
+ d.Byte(valueTypeRecursive)
+ v.expr.dump(d)
+ d.Str(v.origin)
+}
+
+func (v *recursiveVar) Append(_ *Evaluator, s string) (Var, error) {
+ var exp expr
+ if e, ok := v.expr.(expr); ok {
+ exp = append(e, literal(" "))
+ } else {
+ exp = expr{v.expr, literal(" ")}
+ }
+ sv, _, err := parseExpr([]byte(s), nil, parseOp{alloc: true})
+ if err != nil {
+ return nil, err
+ }
+ if aexpr, ok := sv.(expr); ok {
+ exp = append(exp, aexpr...)
+ } else {
+ exp = append(exp, sv)
+ }
+ v.expr = exp
+ return v, nil
+}
+
+func (v *recursiveVar) AppendVar(ev *Evaluator, val Value) (Var, error) {
+ var buf bytes.Buffer
+ buf.WriteString(v.expr.String())
+ buf.WriteByte(' ')
+ buf.WriteString(val.String())
+ e, _, err := parseExpr(buf.Bytes(), nil, parseOp{alloc: true})
+ if err != nil {
+ return nil, err
+ }
+ v.expr = e
+ return v, nil
+}
+
+type undefinedVar struct{}
+
+func (undefinedVar) Flavor() string { return "undefined" }
+func (undefinedVar) Origin() string { return "undefined" }
+func (undefinedVar) IsDefined() bool { return false }
+func (undefinedVar) String() string { return "" }
+func (undefinedVar) Eval(_ evalWriter, _ *Evaluator) error {
+ return nil
+}
+func (undefinedVar) serialize() serializableVar {
+ return serializableVar{Type: "undefined"}
+}
+func (undefinedVar) dump(d *dumpbuf) {
+ d.Byte(valueTypeUndefined)
+}
+
+func (undefinedVar) Append(*Evaluator, string) (Var, error) {
+ return undefinedVar{}, nil
+}
+
+func (undefinedVar) AppendVar(_ *Evaluator, val Value) (Var, error) {
+ return undefinedVar{}, nil
+}
+
+// Vars is a map for make variables.
+type Vars map[string]Var
+
+// usedEnvs tracks what environment variables are used.
+var usedEnvs = map[string]bool{}
+
+// Lookup looks up named make variable.
+func (vt Vars) Lookup(name string) Var {
+ if v, ok := vt[name]; ok {
+ if strings.HasPrefix(v.Origin(), "environment") {
+ usedEnvs[name] = true
+ }
+ return v
+ }
+ return undefinedVar{}
+}
+
+// origin precedence
+// override / environment override
+// command line
+// file
+// environment
+// default
+// TODO(ukai): is this correct order?
+var originPrecedence = map[string]int{
+ "override": 4,
+ "environment override": 4,
+ "command line": 3,
+ "file": 2,
+ "environment": 2,
+ "default": 1,
+ "undefined": 0,
+ "automatic": 0,
+}
+
+// Assign assigns v to name.
+func (vt Vars) Assign(name string, v Var) {
+ vo := v.Origin()
+ // assign automatic always win.
+ // assign new value to automatic always win.
+ if vo != "automatic" {
+ vp := originPrecedence[v.Origin()]
+ var op int
+ if ov, ok := vt[name]; ok {
+ op = originPrecedence[ov.Origin()]
+ }
+ if op > vp {
+ return
+ }
+ }
+ vt[name] = v
+}
+
+// NewVars creates new Vars.
+func NewVars(vt Vars) Vars {
+ r := make(Vars)
+ r.Merge(vt)
+ return r
+}
+
+// Merge merges vt2 into vt.
+func (vt Vars) Merge(vt2 Vars) {
+ for k, v := range vt2 {
+ vt[k] = v
+ }
+}
+
+// save saves value of the variable named name.
+// calling returned value will restore to the old value at the time
+// when save called.
+func (vt Vars) save(name string) func() {
+ if v, ok := vt[name]; ok {
+ return func() {
+ vt[name] = v
+ }
+ }
+ return func() {
+ delete(vt, name)
+ }
+}