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 15package kati 16 17import ( 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. 28type 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. 37func (g *DepGraph) Nodes() []*DepNode { return g.nodes } 38 39// Vars returns all variables. 40func (g *DepGraph) Vars() Vars { return g.vars } 41 42func (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. 72type 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. 82func 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 103func 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. 119func 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. 219type Loader interface { 220 Load(string) (*DepGraph, error) 221} 222 223// Saver is the interface that saves DepGraph. 224type Saver interface { 225 Save(*DepGraph, string, []string) error 226} 227 228// LoadSaver is the interface that groups Load and Save methods. 229type LoadSaver interface { 230 Loader 231 Saver 232} 233