796 lines
18 KiB
Go
796 lines
18 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"
|
|
"crypto/sha1"
|
|
"encoding/binary"
|
|
"encoding/gob"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/url"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/golang/glog"
|
|
)
|
|
|
|
const (
|
|
valueTypeRecursive = 'R'
|
|
valueTypeSimple = 'S'
|
|
valueTypeTSV = 'T'
|
|
valueTypeUndefined = 'U'
|
|
valueTypeAssign = 'a'
|
|
valueTypeExpr = 'e'
|
|
valueTypeFunc = 'f'
|
|
valueTypeLiteral = 'l'
|
|
valueTypeNop = 'n'
|
|
valueTypeParamref = 'p'
|
|
valueTypeVarref = 'r'
|
|
valueTypeVarsubst = 's'
|
|
valueTypeTmpval = 't'
|
|
)
|
|
|
|
// JSON is a json loader/saver.
|
|
var JSON LoadSaver
|
|
|
|
// GOB is a gob loader/saver.
|
|
var GOB LoadSaver
|
|
|
|
func init() {
|
|
JSON = jsonLoadSaver{}
|
|
GOB = gobLoadSaver{}
|
|
}
|
|
|
|
type jsonLoadSaver struct{}
|
|
type gobLoadSaver struct{}
|
|
|
|
type dumpbuf struct {
|
|
w bytes.Buffer
|
|
err error
|
|
}
|
|
|
|
func (d *dumpbuf) Int(i int) {
|
|
if d.err != nil {
|
|
return
|
|
}
|
|
v := int32(i)
|
|
d.err = binary.Write(&d.w, binary.LittleEndian, &v)
|
|
}
|
|
|
|
func (d *dumpbuf) Str(s string) {
|
|
if d.err != nil {
|
|
return
|
|
}
|
|
d.Int(len(s))
|
|
if d.err != nil {
|
|
return
|
|
}
|
|
_, d.err = io.WriteString(&d.w, s)
|
|
}
|
|
|
|
func (d *dumpbuf) Bytes(b []byte) {
|
|
if d.err != nil {
|
|
return
|
|
}
|
|
d.Int(len(b))
|
|
if d.err != nil {
|
|
return
|
|
}
|
|
_, d.err = d.w.Write(b)
|
|
}
|
|
|
|
func (d *dumpbuf) Byte(b byte) {
|
|
if d.err != nil {
|
|
return
|
|
}
|
|
d.err = writeByte(&d.w, b)
|
|
}
|
|
|
|
type serializableVar struct {
|
|
Type string
|
|
V string
|
|
Origin string
|
|
Children []serializableVar
|
|
}
|
|
|
|
type serializableDepNode struct {
|
|
Output int
|
|
Cmds []string
|
|
Deps []int
|
|
OrderOnlys []int
|
|
Parents []int
|
|
HasRule bool
|
|
IsPhony bool
|
|
ActualInputs []int
|
|
TargetSpecificVars []int
|
|
Filename string
|
|
Lineno int
|
|
}
|
|
|
|
type serializableTargetSpecificVar struct {
|
|
Name string
|
|
Value serializableVar
|
|
}
|
|
|
|
type serializableGraph struct {
|
|
Nodes []*serializableDepNode
|
|
Vars map[string]serializableVar
|
|
Tsvs []serializableTargetSpecificVar
|
|
Targets []string
|
|
Roots []string
|
|
AccessedMks []*accessedMakefile
|
|
Exports map[string]bool
|
|
}
|
|
|
|
func encGob(v interface{}) (string, error) {
|
|
var buf bytes.Buffer
|
|
e := gob.NewEncoder(&buf)
|
|
err := e.Encode(v)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
func encVar(k string, v Var) (string, error) {
|
|
var dump dumpbuf
|
|
dump.Str(k)
|
|
v.dump(&dump)
|
|
return dump.w.String(), dump.err
|
|
}
|
|
|
|
type depNodesSerializer struct {
|
|
nodes []*serializableDepNode
|
|
tsvs []serializableTargetSpecificVar
|
|
tsvMap map[string]int
|
|
targets []string
|
|
targetMap map[string]int
|
|
done map[string]bool
|
|
err error
|
|
}
|
|
|
|
func newDepNodesSerializer() *depNodesSerializer {
|
|
return &depNodesSerializer{
|
|
tsvMap: make(map[string]int),
|
|
targetMap: make(map[string]int),
|
|
done: make(map[string]bool),
|
|
}
|
|
}
|
|
|
|
func (ns *depNodesSerializer) serializeTarget(t string) int {
|
|
id, present := ns.targetMap[t]
|
|
if present {
|
|
return id
|
|
}
|
|
id = len(ns.targets)
|
|
ns.targetMap[t] = id
|
|
ns.targets = append(ns.targets, t)
|
|
return id
|
|
}
|
|
|
|
func (ns *depNodesSerializer) serializeDepNodes(nodes []*DepNode) {
|
|
if ns.err != nil {
|
|
return
|
|
}
|
|
for _, n := range nodes {
|
|
if ns.done[n.Output] {
|
|
continue
|
|
}
|
|
ns.done[n.Output] = true
|
|
|
|
var deps []int
|
|
for _, d := range n.Deps {
|
|
deps = append(deps, ns.serializeTarget(d.Output))
|
|
}
|
|
var orderonlys []int
|
|
for _, d := range n.OrderOnlys {
|
|
orderonlys = append(orderonlys, ns.serializeTarget(d.Output))
|
|
}
|
|
var parents []int
|
|
for _, d := range n.Parents {
|
|
parents = append(parents, ns.serializeTarget(d.Output))
|
|
}
|
|
var actualInputs []int
|
|
for _, i := range n.ActualInputs {
|
|
actualInputs = append(actualInputs, ns.serializeTarget(i))
|
|
}
|
|
|
|
// Sort keys for consistent serialization.
|
|
var tsvKeys []string
|
|
for k := range n.TargetSpecificVars {
|
|
tsvKeys = append(tsvKeys, k)
|
|
}
|
|
sort.Strings(tsvKeys)
|
|
|
|
var vars []int
|
|
for _, k := range tsvKeys {
|
|
v := n.TargetSpecificVars[k]
|
|
sv := serializableTargetSpecificVar{Name: k, Value: v.serialize()}
|
|
//gob := encGob(sv)
|
|
gob, err := encVar(k, v)
|
|
if err != nil {
|
|
ns.err = err
|
|
return
|
|
}
|
|
id, present := ns.tsvMap[gob]
|
|
if !present {
|
|
id = len(ns.tsvs)
|
|
ns.tsvMap[gob] = id
|
|
ns.tsvs = append(ns.tsvs, sv)
|
|
}
|
|
vars = append(vars, id)
|
|
}
|
|
|
|
ns.nodes = append(ns.nodes, &serializableDepNode{
|
|
Output: ns.serializeTarget(n.Output),
|
|
Cmds: n.Cmds,
|
|
Deps: deps,
|
|
OrderOnlys: orderonlys,
|
|
Parents: parents,
|
|
HasRule: n.HasRule,
|
|
IsPhony: n.IsPhony,
|
|
ActualInputs: actualInputs,
|
|
TargetSpecificVars: vars,
|
|
Filename: n.Filename,
|
|
Lineno: n.Lineno,
|
|
})
|
|
ns.serializeDepNodes(n.Deps)
|
|
if ns.err != nil {
|
|
return
|
|
}
|
|
ns.serializeDepNodes(n.OrderOnlys)
|
|
if ns.err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func makeSerializableVars(vars Vars) (r map[string]serializableVar) {
|
|
r = make(map[string]serializableVar)
|
|
for k, v := range vars {
|
|
r[k] = v.serialize()
|
|
}
|
|
return r
|
|
}
|
|
|
|
func makeSerializableGraph(g *DepGraph, roots []string) (serializableGraph, error) {
|
|
ns := newDepNodesSerializer()
|
|
ns.serializeDepNodes(g.nodes)
|
|
v := makeSerializableVars(g.vars)
|
|
return serializableGraph{
|
|
Nodes: ns.nodes,
|
|
Vars: v,
|
|
Tsvs: ns.tsvs,
|
|
Targets: ns.targets,
|
|
Roots: roots,
|
|
AccessedMks: g.accessedMks,
|
|
Exports: g.exports,
|
|
}, ns.err
|
|
}
|
|
|
|
func (jsonLoadSaver) Save(g *DepGraph, filename string, roots []string) error {
|
|
startTime := time.Now()
|
|
sg, err := makeSerializableGraph(g, roots)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
o, err := json.MarshalIndent(sg, " ", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f, err := os.Create(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = f.Write(o)
|
|
if err != nil {
|
|
f.Close()
|
|
return err
|
|
}
|
|
err = f.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
logStats("json serialize time: %q", time.Since(startTime))
|
|
return nil
|
|
}
|
|
|
|
func (gobLoadSaver) Save(g *DepGraph, filename string, roots []string) error {
|
|
startTime := time.Now()
|
|
f, err := os.Create(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
e := gob.NewEncoder(f)
|
|
var sg serializableGraph
|
|
{
|
|
startTime := time.Now()
|
|
sg, err = makeSerializableGraph(g, roots)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
logStats("gob serialize prepare time: %q", time.Since(startTime))
|
|
}
|
|
{
|
|
startTime := time.Now()
|
|
err = e.Encode(sg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
logStats("gob serialize output time: %q", time.Since(startTime))
|
|
}
|
|
err = f.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
logStats("gob serialize time: %q", time.Since(startTime))
|
|
return nil
|
|
}
|
|
|
|
func cacheFilename(mk string, roots []string) string {
|
|
filename := ".kati_cache." + mk
|
|
for _, r := range roots {
|
|
filename += "." + r
|
|
}
|
|
return url.QueryEscape(filename)
|
|
}
|
|
|
|
func saveCache(g *DepGraph, roots []string) error {
|
|
if len(g.accessedMks) == 0 {
|
|
return fmt.Errorf("no Makefile is read")
|
|
}
|
|
cacheFile := cacheFilename(g.accessedMks[0].Filename, roots)
|
|
for _, mk := range g.accessedMks {
|
|
// Inconsistent, do not dump this result.
|
|
if mk.State == fileInconsistent {
|
|
if exists(cacheFile) {
|
|
os.Remove(cacheFile)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
return GOB.Save(g, cacheFile, roots)
|
|
}
|
|
|
|
func deserializeSingleChild(sv serializableVar) (Value, error) {
|
|
if len(sv.Children) != 1 {
|
|
return nil, fmt.Errorf("unexpected number of children: %q", sv)
|
|
}
|
|
return deserializeVar(sv.Children[0])
|
|
}
|
|
|
|
func deserializeVar(sv serializableVar) (r Value, err error) {
|
|
switch sv.Type {
|
|
case "literal":
|
|
return literal(sv.V), nil
|
|
case "tmpval":
|
|
return tmpval([]byte(sv.V)), nil
|
|
case "expr":
|
|
var e expr
|
|
for _, v := range sv.Children {
|
|
dv, err := deserializeVar(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
e = append(e, dv)
|
|
}
|
|
return e, nil
|
|
case "varref":
|
|
dv, err := deserializeSingleChild(sv)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &varref{varname: dv, paren: sv.V[0]}, nil
|
|
case "paramref":
|
|
v, err := strconv.Atoi(sv.V)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return paramref(v), nil
|
|
case "varsubst":
|
|
varname, err := deserializeVar(sv.Children[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pat, err := deserializeVar(sv.Children[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
subst, err := deserializeVar(sv.Children[2])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return varsubst{
|
|
varname: varname,
|
|
pat: pat,
|
|
subst: subst,
|
|
paren: sv.V[0],
|
|
}, nil
|
|
|
|
case "func":
|
|
dv, err := deserializeVar(sv.Children[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
name, ok := dv.(literal)
|
|
if !ok {
|
|
return nil, fmt.Errorf("func name is not literal %s: %T", dv, dv)
|
|
}
|
|
f := funcMap[string(name[1:])]()
|
|
f.AddArg(name)
|
|
for _, a := range sv.Children[1:] {
|
|
dv, err := deserializeVar(a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
f.AddArg(dv)
|
|
}
|
|
return f, nil
|
|
case "funcEvalAssign":
|
|
rhs, err := deserializeVar(sv.Children[2])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &funcEvalAssign{
|
|
lhs: sv.Children[0].V,
|
|
op: sv.Children[1].V,
|
|
rhs: rhs,
|
|
}, nil
|
|
case "funcNop":
|
|
return &funcNop{expr: sv.V}, nil
|
|
|
|
case "simple":
|
|
return &simpleVar{
|
|
value: strings.Split(sv.V, " "),
|
|
origin: sv.Origin,
|
|
}, nil
|
|
case "recursive":
|
|
expr, err := deserializeSingleChild(sv)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &recursiveVar{
|
|
expr: expr,
|
|
origin: sv.Origin,
|
|
}, nil
|
|
|
|
case ":=", "=", "+=", "?=":
|
|
dv, err := deserializeSingleChild(sv)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
v, ok := dv.(Var)
|
|
if !ok {
|
|
return nil, fmt.Errorf("not var: target specific var %s %T", dv, dv)
|
|
}
|
|
return &targetSpecificVar{
|
|
v: v,
|
|
op: sv.Type,
|
|
}, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unknown serialized variable type: %q", sv)
|
|
}
|
|
}
|
|
|
|
func deserializeVars(vars map[string]serializableVar) (Vars, error) {
|
|
r := make(Vars)
|
|
for k, v := range vars {
|
|
dv, err := deserializeVar(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
vv, ok := dv.(Var)
|
|
if !ok {
|
|
return nil, fmt.Errorf("not var: %s: %T", dv, dv)
|
|
}
|
|
r[k] = vv
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
func deserializeNodes(g serializableGraph) (r []*DepNode, err error) {
|
|
nodes := g.Nodes
|
|
tsvs := g.Tsvs
|
|
targets := g.Targets
|
|
// Deserialize all TSVs first so that multiple rules can share memory.
|
|
var tsvValues []Var
|
|
for _, sv := range tsvs {
|
|
dv, err := deserializeVar(sv.Value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
vv, ok := dv.(Var)
|
|
if !ok {
|
|
return nil, fmt.Errorf("not var: %s %T", dv, dv)
|
|
}
|
|
tsvValues = append(tsvValues, vv)
|
|
}
|
|
|
|
nodeMap := make(map[string]*DepNode)
|
|
for _, n := range nodes {
|
|
var actualInputs []string
|
|
for _, i := range n.ActualInputs {
|
|
actualInputs = append(actualInputs, targets[i])
|
|
}
|
|
|
|
d := &DepNode{
|
|
Output: targets[n.Output],
|
|
Cmds: n.Cmds,
|
|
HasRule: n.HasRule,
|
|
IsPhony: n.IsPhony,
|
|
ActualInputs: actualInputs,
|
|
Filename: n.Filename,
|
|
Lineno: n.Lineno,
|
|
TargetSpecificVars: make(Vars),
|
|
}
|
|
|
|
for _, id := range n.TargetSpecificVars {
|
|
sv := tsvs[id]
|
|
d.TargetSpecificVars[sv.Name] = tsvValues[id]
|
|
}
|
|
|
|
nodeMap[targets[n.Output]] = d
|
|
r = append(r, d)
|
|
}
|
|
|
|
for _, n := range nodes {
|
|
d := nodeMap[targets[n.Output]]
|
|
for _, o := range n.Deps {
|
|
c, present := nodeMap[targets[o]]
|
|
if !present {
|
|
return nil, fmt.Errorf("unknown target: %d (%s)", o, targets[o])
|
|
}
|
|
d.Deps = append(d.Deps, c)
|
|
}
|
|
for _, o := range n.OrderOnlys {
|
|
c, present := nodeMap[targets[o]]
|
|
if !present {
|
|
return nil, fmt.Errorf("unknown target: %d (%s)", o, targets[o])
|
|
}
|
|
d.OrderOnlys = append(d.OrderOnlys, c)
|
|
}
|
|
for _, o := range n.Parents {
|
|
c, present := nodeMap[targets[o]]
|
|
if !present {
|
|
return nil, fmt.Errorf("unknown target: %d (%s)", o, targets[o])
|
|
}
|
|
d.Parents = append(d.Parents, c)
|
|
}
|
|
}
|
|
|
|
return r, nil
|
|
}
|
|
|
|
func human(n int) string {
|
|
if n >= 10*1000*1000*1000 {
|
|
return fmt.Sprintf("%.2fGB", float32(n)/1000/1000/1000)
|
|
}
|
|
if n >= 10*1000*1000 {
|
|
return fmt.Sprintf("%.2fMB", float32(n)/1000/1000)
|
|
}
|
|
if n >= 10*1000 {
|
|
return fmt.Sprintf("%.2fkB", float32(n)/1000)
|
|
}
|
|
return fmt.Sprintf("%dB", n)
|
|
}
|
|
|
|
func showSerializedNodesStats(nodes []*serializableDepNode) {
|
|
outputSize := 0
|
|
cmdSize := 0
|
|
depsSize := 0
|
|
orderOnlysSize := 0
|
|
actualInputSize := 0
|
|
tsvSize := 0
|
|
filenameSize := 0
|
|
linenoSize := 0
|
|
for _, n := range nodes {
|
|
outputSize += 4
|
|
for _, c := range n.Cmds {
|
|
cmdSize += len(c)
|
|
}
|
|
depsSize += 4 * len(n.Deps)
|
|
orderOnlysSize += 4 * len(n.OrderOnlys)
|
|
actualInputSize += 4 * len(n.ActualInputs)
|
|
tsvSize += 4 * len(n.TargetSpecificVars)
|
|
filenameSize += len(n.Filename)
|
|
linenoSize += 4
|
|
}
|
|
size := outputSize + cmdSize + depsSize + orderOnlysSize + actualInputSize + tsvSize + filenameSize + linenoSize
|
|
logStats("%d nodes %s", len(nodes), human(size))
|
|
logStats(" output %s", human(outputSize))
|
|
logStats(" command %s", human(cmdSize))
|
|
logStats(" deps %s", human(depsSize))
|
|
logStats(" orderonlys %s", human(orderOnlysSize))
|
|
logStats(" inputs %s", human(actualInputSize))
|
|
logStats(" tsv %s", human(tsvSize))
|
|
logStats(" filename %s", human(filenameSize))
|
|
logStats(" lineno %s", human(linenoSize))
|
|
}
|
|
|
|
func (v serializableVar) size() int {
|
|
size := 0
|
|
size += len(v.Type)
|
|
size += len(v.V)
|
|
size += len(v.Origin)
|
|
for _, c := range v.Children {
|
|
size += c.size()
|
|
}
|
|
return size
|
|
}
|
|
|
|
func showSerializedVarsStats(vars map[string]serializableVar) {
|
|
nameSize := 0
|
|
valueSize := 0
|
|
for k, v := range vars {
|
|
nameSize += len(k)
|
|
valueSize += v.size()
|
|
}
|
|
size := nameSize + valueSize
|
|
logStats("%d vars %s", len(vars), human(size))
|
|
logStats(" name %s", human(nameSize))
|
|
logStats(" value %s", human(valueSize))
|
|
}
|
|
|
|
func showSerializedTsvsStats(vars []serializableTargetSpecificVar) {
|
|
nameSize := 0
|
|
valueSize := 0
|
|
for _, v := range vars {
|
|
nameSize += len(v.Name)
|
|
valueSize += v.Value.size()
|
|
}
|
|
size := nameSize + valueSize
|
|
logStats("%d tsvs %s", len(vars), human(size))
|
|
logStats(" name %s", human(nameSize))
|
|
logStats(" value %s", human(valueSize))
|
|
}
|
|
|
|
func showSerializedTargetsStats(targets []string) {
|
|
size := 0
|
|
for _, t := range targets {
|
|
size += len(t)
|
|
}
|
|
logStats("%d targets %s", len(targets), human(size))
|
|
}
|
|
|
|
func showSerializedAccessedMksStats(accessedMks []*accessedMakefile) {
|
|
size := 0
|
|
for _, rm := range accessedMks {
|
|
size += len(rm.Filename) + len(rm.Hash) + 4
|
|
}
|
|
logStats("%d makefiles %s", len(accessedMks), human(size))
|
|
}
|
|
|
|
func showSerializedGraphStats(g serializableGraph) {
|
|
showSerializedNodesStats(g.Nodes)
|
|
showSerializedVarsStats(g.Vars)
|
|
showSerializedTsvsStats(g.Tsvs)
|
|
showSerializedTargetsStats(g.Targets)
|
|
showSerializedAccessedMksStats(g.AccessedMks)
|
|
}
|
|
|
|
func deserializeGraph(g serializableGraph) (*DepGraph, error) {
|
|
if StatsFlag {
|
|
showSerializedGraphStats(g)
|
|
}
|
|
nodes, err := deserializeNodes(g)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
vars, err := deserializeVars(g.Vars)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &DepGraph{
|
|
nodes: nodes,
|
|
vars: vars,
|
|
accessedMks: g.AccessedMks,
|
|
exports: g.Exports,
|
|
}, nil
|
|
}
|
|
|
|
func (jsonLoadSaver) Load(filename string) (*DepGraph, error) {
|
|
startTime := time.Now()
|
|
f, err := os.Open(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
d := json.NewDecoder(f)
|
|
g := serializableGraph{Vars: make(map[string]serializableVar)}
|
|
err = d.Decode(&g)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dg, err := deserializeGraph(g)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
logStats("gob deserialize time: %q", time.Since(startTime))
|
|
return dg, nil
|
|
}
|
|
|
|
func (gobLoadSaver) Load(filename string) (*DepGraph, error) {
|
|
startTime := time.Now()
|
|
f, err := os.Open(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
d := gob.NewDecoder(f)
|
|
g := serializableGraph{Vars: make(map[string]serializableVar)}
|
|
err = d.Decode(&g)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dg, err := deserializeGraph(g)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
logStats("json deserialize time: %q", time.Since(startTime))
|
|
return dg, nil
|
|
}
|
|
|
|
func loadCache(makefile string, roots []string) (*DepGraph, error) {
|
|
startTime := time.Now()
|
|
defer func() {
|
|
logStats("Cache lookup time: %q", time.Since(startTime))
|
|
}()
|
|
|
|
filename := cacheFilename(makefile, roots)
|
|
if !exists(filename) {
|
|
glog.Warningf("Cache not found %q", filename)
|
|
return nil, fmt.Errorf("cache not found: %s", filename)
|
|
}
|
|
|
|
g, err := GOB.Load(filename)
|
|
if err != nil {
|
|
glog.Warning("Cache load error %q: %v", filename, err)
|
|
return nil, err
|
|
}
|
|
for _, mk := range g.accessedMks {
|
|
if mk.State != fileExists && mk.State != fileNotExists {
|
|
return nil, fmt.Errorf("internal error: broken state: %d", mk.State)
|
|
}
|
|
if mk.State == fileNotExists {
|
|
if exists(mk.Filename) {
|
|
glog.Infof("Cache expired: %s", mk.Filename)
|
|
return nil, fmt.Errorf("cache expired: %s", mk.Filename)
|
|
}
|
|
} else {
|
|
c, err := ioutil.ReadFile(mk.Filename)
|
|
if err != nil {
|
|
glog.Infof("Cache expired: %s", mk.Filename)
|
|
return nil, fmt.Errorf("cache expired: %s", mk.Filename)
|
|
}
|
|
h := sha1.Sum(c)
|
|
if !bytes.Equal(h[:], mk.Hash[:]) {
|
|
glog.Infof("Cache expired: %s", mk.Filename)
|
|
return nil, fmt.Errorf("cache expired: %s", mk.Filename)
|
|
}
|
|
}
|
|
}
|
|
glog.Info("Cache found in %q", filename)
|
|
return g, nil
|
|
}
|