diff options
Diffstat (limited to 'golang/kati/depgraph.go')
| -rw-r--r-- | golang/kati/depgraph.go | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/golang/kati/depgraph.go b/golang/kati/depgraph.go new file mode 100644 index 0000000..5b32287 --- /dev/null +++ b/golang/kati/depgraph.go @@ -0,0 +1,232 @@ +// 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 ( + "crypto/sha1" + "fmt" + "io/ioutil" + "strings" + "time" + + "github.com/golang/glog" +) + +// DepGraph represents rules defined in makefiles. +type DepGraph struct { + nodes []*DepNode + vars Vars + accessedMks []*accessedMakefile + exports map[string]bool + vpaths searchPaths +} + +// Nodes returns all rules. +func (g *DepGraph) Nodes() []*DepNode { return g.nodes } + +// Vars returns all variables. +func (g *DepGraph) Vars() Vars { return g.vars } + +func (g *DepGraph) resolveVPATH() { + seen := make(map[*DepNode]bool) + var fix func(n *DepNode) + fix = func(n *DepNode) { + if seen[n] { + return + } + seen[n] = true + glog.V(3).Infof("vpath check %s [%#v]", n.Output, g.vpaths) + if output, ok := g.vpaths.exists(n.Output); ok { + glog.V(2).Infof("vpath fix %s=>%s", n.Output, output) + n.Output = output + } + for _, d := range n.Deps { + fix(d) + } + for _, d := range n.OrderOnlys { + fix(d) + } + for _, d := range n.Parents { + fix(d) + } + // fix ActualInputs? + } + for _, n := range g.nodes { + fix(n) + } +} + +// LoadReq is a request to load makefile. +type LoadReq struct { + Makefile string + Targets []string + CommandLineVars []string + EnvironmentVars []string + UseCache bool + EagerEvalCommand bool +} + +// FromCommandLine creates LoadReq from given command line. +func FromCommandLine(cmdline []string) LoadReq { + var vars []string + var targets []string + for _, arg := range cmdline { + if strings.IndexByte(arg, '=') >= 0 { + vars = append(vars, arg) + continue + } + targets = append(targets, arg) + } + mk, err := defaultMakefile() + if err != nil { + glog.Warningf("default makefile: %v", err) + } + return LoadReq{ + Makefile: mk, + Targets: targets, + CommandLineVars: vars, + } +} + +func initVars(vars Vars, kvlist []string, origin string) error { + for _, v := range kvlist { + kv := strings.SplitN(v, "=", 2) + glog.V(1).Infof("%s var %q", origin, v) + if len(kv) < 2 { + return fmt.Errorf("A weird %s variable %q", origin, kv) + } + vars.Assign(kv[0], &recursiveVar{ + expr: literal(kv[1]), + origin: origin, + }) + } + return nil +} + +// Load loads makefile. +func Load(req LoadReq) (*DepGraph, error) { + startTime := time.Now() + var err error + if req.Makefile == "" { + req.Makefile, err = defaultMakefile() + if err != nil { + return nil, err + } + } + + if req.UseCache { + g, err := loadCache(req.Makefile, req.Targets) + if err == nil { + return g, nil + } + } + + bmk, err := bootstrapMakefile(req.Targets) + if err != nil { + return nil, err + } + + content, err := ioutil.ReadFile(req.Makefile) + if err != nil { + return nil, err + } + mk, err := parseMakefile(content, req.Makefile) + if err != nil { + return nil, err + } + + for _, stmt := range mk.stmts { + stmt.show() + } + + mk.stmts = append(bmk.stmts, mk.stmts...) + + vars := make(Vars) + err = initVars(vars, req.EnvironmentVars, "environment") + if err != nil { + return nil, err + } + err = initVars(vars, req.CommandLineVars, "command line") + if err != nil { + return nil, err + } + er, err := eval(mk, vars, req.UseCache) + if err != nil { + return nil, err + } + vars.Merge(er.vars) + + logStats("eval time: %q", time.Since(startTime)) + logStats("shell func time: %q %d", shellStats.Duration(), shellStats.Count()) + + startTime = time.Now() + db, err := newDepBuilder(er, vars) + if err != nil { + return nil, err + } + logStats("dep build prepare time: %q", time.Since(startTime)) + + startTime = time.Now() + nodes, err := db.Eval(req.Targets) + if err != nil { + return nil, err + } + logStats("dep build time: %q", time.Since(startTime)) + var accessedMks []*accessedMakefile + // Always put the root Makefile as the first element. + accessedMks = append(accessedMks, &accessedMakefile{ + Filename: req.Makefile, + Hash: sha1.Sum(content), + State: fileExists, + }) + accessedMks = append(accessedMks, er.accessedMks...) + gd := &DepGraph{ + nodes: nodes, + vars: vars, + accessedMks: accessedMks, + exports: er.exports, + vpaths: er.vpaths, + } + if req.EagerEvalCommand { + startTime := time.Now() + err = evalCommands(nodes, vars) + if err != nil { + return nil, err + } + logStats("eager eval command time: %q", time.Since(startTime)) + } + if req.UseCache { + startTime := time.Now() + saveCache(gd, req.Targets) + logStats("serialize time: %q", time.Since(startTime)) + } + return gd, nil +} + +// Loader is the interface that loads DepGraph. +type Loader interface { + Load(string) (*DepGraph, error) +} + +// Saver is the interface that saves DepGraph. +type Saver interface { + Save(*DepGraph, string, []string) error +} + +// LoadSaver is the interface that groups Load and Save methods. +type LoadSaver interface { + Loader + Saver +} |
