371 lines
8.4 KiB
Go
371 lines
8.4 KiB
Go
// 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)
|
|
}
|
|
}
|