Home | History | Annotate | Download | only in kati
      1 // Copyright 2015 Google Inc. All rights reserved
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package kati
     16 
     17 import (
     18 	"crypto/sha1"
     19 	"fmt"
     20 	"io/ioutil"
     21 	"strings"
     22 	"time"
     23 
     24 	"github.com/golang/glog"
     25 )
     26 
     27 // DepGraph represents rules defined in makefiles.
     28 type DepGraph struct {
     29 	nodes       []*DepNode
     30 	vars        Vars
     31 	accessedMks []*accessedMakefile
     32 	exports     map[string]bool
     33 	vpaths      searchPaths
     34 }
     35 
     36 // Nodes returns all rules.
     37 func (g *DepGraph) Nodes() []*DepNode { return g.nodes }
     38 
     39 // Vars returns all variables.
     40 func (g *DepGraph) Vars() Vars { return g.vars }
     41 
     42 func (g *DepGraph) resolveVPATH() {
     43 	seen := make(map[*DepNode]bool)
     44 	var fix func(n *DepNode)
     45 	fix = func(n *DepNode) {
     46 		if seen[n] {
     47 			return
     48 		}
     49 		seen[n] = true
     50 		glog.V(3).Infof("vpath check %s [%#v]", n.Output, g.vpaths)
     51 		if output, ok := g.vpaths.exists(n.Output); ok {
     52 			glog.V(2).Infof("vpath fix %s=>%s", n.Output, output)
     53 			n.Output = output
     54 		}
     55 		for _, d := range n.Deps {
     56 			fix(d)
     57 		}
     58 		for _, d := range n.OrderOnlys {
     59 			fix(d)
     60 		}
     61 		for _, d := range n.Parents {
     62 			fix(d)
     63 		}
     64 		// fix ActualInputs?
     65 	}
     66 	for _, n := range g.nodes {
     67 		fix(n)
     68 	}
     69 }
     70 
     71 // LoadReq is a request to load makefile.
     72 type LoadReq struct {
     73 	Makefile         string
     74 	Targets          []string
     75 	CommandLineVars  []string
     76 	EnvironmentVars  []string
     77 	UseCache         bool
     78 	EagerEvalCommand bool
     79 }
     80 
     81 // FromCommandLine creates LoadReq from given command line.
     82 func FromCommandLine(cmdline []string) LoadReq {
     83 	var vars []string
     84 	var targets []string
     85 	for _, arg := range cmdline {
     86 		if strings.IndexByte(arg, '=') >= 0 {
     87 			vars = append(vars, arg)
     88 			continue
     89 		}
     90 		targets = append(targets, arg)
     91 	}
     92 	mk, err := defaultMakefile()
     93 	if err != nil {
     94 		glog.Warningf("default makefile: %v", err)
     95 	}
     96 	return LoadReq{
     97 		Makefile:        mk,
     98 		Targets:         targets,
     99 		CommandLineVars: vars,
    100 	}
    101 }
    102 
    103 func initVars(vars Vars, kvlist []string, origin string) error {
    104 	for _, v := range kvlist {
    105 		kv := strings.SplitN(v, "=", 2)
    106 		glog.V(1).Infof("%s var %q", origin, v)
    107 		if len(kv) < 2 {
    108 			return fmt.Errorf("A weird %s variable %q", origin, kv)
    109 		}
    110 		vars.Assign(kv[0], &recursiveVar{
    111 			expr:   literal(kv[1]),
    112 			origin: origin,
    113 		})
    114 	}
    115 	return nil
    116 }
    117 
    118 // Load loads makefile.
    119 func Load(req LoadReq) (*DepGraph, error) {
    120 	startTime := time.Now()
    121 	var err error
    122 	if req.Makefile == "" {
    123 		req.Makefile, err = defaultMakefile()
    124 		if err != nil {
    125 			return nil, err
    126 		}
    127 	}
    128 
    129 	if req.UseCache {
    130 		g, err := loadCache(req.Makefile, req.Targets)
    131 		if err == nil {
    132 			return g, nil
    133 		}
    134 	}
    135 
    136 	bmk, err := bootstrapMakefile(req.Targets)
    137 	if err != nil {
    138 		return nil, err
    139 	}
    140 
    141 	content, err := ioutil.ReadFile(req.Makefile)
    142 	if err != nil {
    143 		return nil, err
    144 	}
    145 	mk, err := parseMakefile(content, req.Makefile)
    146 	if err != nil {
    147 		return nil, err
    148 	}
    149 
    150 	for _, stmt := range mk.stmts {
    151 		stmt.show()
    152 	}
    153 
    154 	mk.stmts = append(bmk.stmts, mk.stmts...)
    155 
    156 	vars := make(Vars)
    157 	err = initVars(vars, req.EnvironmentVars, "environment")
    158 	if err != nil {
    159 		return nil, err
    160 	}
    161 	err = initVars(vars, req.CommandLineVars, "command line")
    162 	if err != nil {
    163 		return nil, err
    164 	}
    165 	er, err := eval(mk, vars, req.UseCache)
    166 	if err != nil {
    167 		return nil, err
    168 	}
    169 	vars.Merge(er.vars)
    170 
    171 	logStats("eval time: %q", time.Since(startTime))
    172 	logStats("shell func time: %q %d", shellStats.Duration(), shellStats.Count())
    173 
    174 	startTime = time.Now()
    175 	db, err := newDepBuilder(er, vars)
    176 	if err != nil {
    177 		return nil, err
    178 	}
    179 	logStats("dep build prepare time: %q", time.Since(startTime))
    180 
    181 	startTime = time.Now()
    182 	nodes, err := db.Eval(req.Targets)
    183 	if err != nil {
    184 		return nil, err
    185 	}
    186 	logStats("dep build time: %q", time.Since(startTime))
    187 	var accessedMks []*accessedMakefile
    188 	// Always put the root Makefile as the first element.
    189 	accessedMks = append(accessedMks, &accessedMakefile{
    190 		Filename: req.Makefile,
    191 		Hash:     sha1.Sum(content),
    192 		State:    fileExists,
    193 	})
    194 	accessedMks = append(accessedMks, er.accessedMks...)
    195 	gd := &DepGraph{
    196 		nodes:       nodes,
    197 		vars:        vars,
    198 		accessedMks: accessedMks,
    199 		exports:     er.exports,
    200 		vpaths:      er.vpaths,
    201 	}
    202 	if req.EagerEvalCommand {
    203 		startTime := time.Now()
    204 		err = evalCommands(nodes, vars)
    205 		if err != nil {
    206 			return nil, err
    207 		}
    208 		logStats("eager eval command time: %q", time.Since(startTime))
    209 	}
    210 	if req.UseCache {
    211 		startTime := time.Now()
    212 		saveCache(gd, req.Targets)
    213 		logStats("serialize time: %q", time.Since(startTime))
    214 	}
    215 	return gd, nil
    216 }
    217 
    218 // Loader is the interface that loads DepGraph.
    219 type Loader interface {
    220 	Load(string) (*DepGraph, error)
    221 }
    222 
    223 // Saver is the interface that saves DepGraph.
    224 type Saver interface {
    225 	Save(*DepGraph, string, []string) error
    226 }
    227 
    228 // LoadSaver is the interface that groups Load and Save methods.
    229 type LoadSaver interface {
    230 	Loader
    231 	Saver
    232 }
    233