blob: 5e7e996f2a2f8cde03c85a91b87829e27ca7d7c1 [file] [log] [blame]
// 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)
}
}