Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 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" |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 19 | "fmt" |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 20 | "io/ioutil" |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 21 | "strings" |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 22 | "time" |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 23 | |
| 24 | "github.com/golang/glog" |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 25 | ) |
| 26 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 27 | // DepGraph represents rules defined in makefiles. |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 28 | type DepGraph struct { |
Fumitoshi Ukai | 0af4452 | 2015-06-25 15:26:08 +0900 | [diff] [blame] | 29 | nodes []*DepNode |
| 30 | vars Vars |
| 31 | accessedMks []*accessedMakefile |
| 32 | exports map[string]bool |
Fumitoshi Ukai | 09fcd52 | 2015-07-15 14:31:50 +0900 | [diff] [blame] | 33 | vpaths searchPaths |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 34 | } |
| 35 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 36 | // Nodes returns all rules. |
| 37 | func (g *DepGraph) Nodes() []*DepNode { return g.nodes } |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 38 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 39 | // Vars returns all variables. |
| 40 | func (g *DepGraph) Vars() Vars { return g.vars } |
| 41 | |
Fumitoshi Ukai | 8a1110d | 2015-07-29 13:18:40 +0900 | [diff] [blame] | 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 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 71 | // LoadReq is a request to load makefile. |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 72 | type LoadReq struct { |
Fumitoshi Ukai | 5c85040 | 2015-06-26 09:52:55 +0900 | [diff] [blame] | 73 | Makefile string |
| 74 | Targets []string |
| 75 | CommandLineVars []string |
| 76 | EnvironmentVars []string |
| 77 | UseCache bool |
| 78 | EagerEvalCommand bool |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 79 | } |
| 80 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 81 | // FromCommandLine creates LoadReq from given command line. |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 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 | } |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 92 | mk, err := defaultMakefile() |
| 93 | if err != nil { |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 94 | glog.Warningf("default makefile: %v", err) |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 95 | } |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 96 | return LoadReq{ |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 97 | Makefile: mk, |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 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) |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 106 | glog.V(1).Infof("%s var %q", origin, v) |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 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 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 118 | // Load loads makefile. |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 119 | func Load(req LoadReq) (*DepGraph, error) { |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 120 | startTime := time.Now() |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 121 | var err error |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 122 | if req.Makefile == "" { |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 123 | req.Makefile, err = defaultMakefile() |
| 124 | if err != nil { |
| 125 | return nil, err |
| 126 | } |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 127 | } |
| 128 | |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 129 | if req.UseCache { |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 130 | g, err := loadCache(req.Makefile, req.Targets) |
| 131 | if err == nil { |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 132 | return g, nil |
| 133 | } |
| 134 | } |
| 135 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 136 | bmk, err := bootstrapMakefile(req.Targets) |
| 137 | if err != nil { |
| 138 | return nil, err |
| 139 | } |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 140 | |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 141 | content, err := ioutil.ReadFile(req.Makefile) |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 142 | if err != nil { |
| 143 | return nil, err |
| 144 | } |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 145 | mk, err := parseMakefile(content, req.Makefile) |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 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) |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 157 | err = initVars(vars, req.EnvironmentVars, "environment") |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 158 | if err != nil { |
| 159 | return nil, err |
| 160 | } |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 161 | err = initVars(vars, req.CommandLineVars, "command line") |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 162 | if err != nil { |
| 163 | return nil, err |
| 164 | } |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 165 | er, err := eval(mk, vars, req.UseCache) |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 166 | if err != nil { |
| 167 | return nil, err |
| 168 | } |
| 169 | vars.Merge(er.vars) |
| 170 | |
Fumitoshi Ukai | 49599e5 | 2015-06-26 10:10:24 +0900 | [diff] [blame] | 171 | logStats("eval time: %q", time.Since(startTime)) |
| 172 | logStats("shell func time: %q %d", shellStats.Duration(), shellStats.Count()) |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 173 | |
| 174 | startTime = time.Now() |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 175 | db, err := newDepBuilder(er, vars) |
| 176 | if err != nil { |
| 177 | return nil, err |
| 178 | } |
Fumitoshi Ukai | 49599e5 | 2015-06-26 10:10:24 +0900 | [diff] [blame] | 179 | logStats("dep build prepare time: %q", time.Since(startTime)) |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 180 | |
| 181 | startTime = time.Now() |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 182 | nodes, err := db.Eval(req.Targets) |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 183 | if err != nil { |
| 184 | return nil, err |
| 185 | } |
Fumitoshi Ukai | 49599e5 | 2015-06-26 10:10:24 +0900 | [diff] [blame] | 186 | logStats("dep build time: %q", time.Since(startTime)) |
Fumitoshi Ukai | 0af4452 | 2015-06-25 15:26:08 +0900 | [diff] [blame] | 187 | var accessedMks []*accessedMakefile |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 188 | // Always put the root Makefile as the first element. |
Fumitoshi Ukai | 0af4452 | 2015-06-25 15:26:08 +0900 | [diff] [blame] | 189 | accessedMks = append(accessedMks, &accessedMakefile{ |
Fumitoshi Ukai | 87586e5 | 2015-06-25 17:49:35 +0900 | [diff] [blame] | 190 | Filename: req.Makefile, |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 191 | Hash: sha1.Sum(content), |
Fumitoshi Ukai | 0af4452 | 2015-06-25 15:26:08 +0900 | [diff] [blame] | 192 | State: fileExists, |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 193 | }) |
Fumitoshi Ukai | 0af4452 | 2015-06-25 15:26:08 +0900 | [diff] [blame] | 194 | accessedMks = append(accessedMks, er.accessedMks...) |
Fumitoshi Ukai | 5c85040 | 2015-06-26 09:52:55 +0900 | [diff] [blame] | 195 | gd := &DepGraph{ |
Fumitoshi Ukai | 0af4452 | 2015-06-25 15:26:08 +0900 | [diff] [blame] | 196 | nodes: nodes, |
| 197 | vars: vars, |
| 198 | accessedMks: accessedMks, |
| 199 | exports: er.exports, |
Fumitoshi Ukai | 09fcd52 | 2015-07-15 14:31:50 +0900 | [diff] [blame] | 200 | vpaths: er.vpaths, |
Fumitoshi Ukai | 5c85040 | 2015-06-26 09:52:55 +0900 | [diff] [blame] | 201 | } |
| 202 | if req.EagerEvalCommand { |
| 203 | startTime := time.Now() |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 204 | err = evalCommands(nodes, vars) |
| 205 | if err != nil { |
| 206 | return nil, err |
| 207 | } |
Fumitoshi Ukai | 49599e5 | 2015-06-26 10:10:24 +0900 | [diff] [blame] | 208 | logStats("eager eval command time: %q", time.Since(startTime)) |
Fumitoshi Ukai | 5c85040 | 2015-06-26 09:52:55 +0900 | [diff] [blame] | 209 | } |
| 210 | if req.UseCache { |
| 211 | startTime := time.Now() |
| 212 | saveCache(gd, req.Targets) |
Fumitoshi Ukai | 49599e5 | 2015-06-26 10:10:24 +0900 | [diff] [blame] | 213 | logStats("serialize time: %q", time.Since(startTime)) |
Fumitoshi Ukai | 5c85040 | 2015-06-26 09:52:55 +0900 | [diff] [blame] | 214 | } |
| 215 | return gd, nil |
Fumitoshi Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 216 | } |
Fumitoshi Ukai | 4bb4cd5 | 2015-06-25 18:07:21 +0900 | [diff] [blame] | 217 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 218 | // Loader is the interface that loads DepGraph. |
Fumitoshi Ukai | 4bb4cd5 | 2015-06-25 18:07:21 +0900 | [diff] [blame] | 219 | type Loader interface { |
| 220 | Load(string) (*DepGraph, error) |
| 221 | } |
| 222 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 223 | // Saver is the interface that saves DepGraph. |
Fumitoshi Ukai | 4bb4cd5 | 2015-06-25 18:07:21 +0900 | [diff] [blame] | 224 | type Saver interface { |
| 225 | Save(*DepGraph, string, []string) error |
| 226 | } |
| 227 | |
Fumitoshi Ukai | 65c7233 | 2015-06-26 21:32:50 +0900 | [diff] [blame] | 228 | // LoadSaver is the interface that groups Load and Save methods. |
Fumitoshi Ukai | 4bb4cd5 | 2015-06-25 18:07:21 +0900 | [diff] [blame] | 229 | type LoadSaver interface { |
| 230 | Loader |
| 231 | Saver |
| 232 | } |