blob: 5b32287c5d4ddf916036a206827e59244597dbd5 [file] [log] [blame]
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +09001// 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"
Fumitoshi Ukai87586e52015-06-25 17:49:35 +090019 "fmt"
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090020 "io/ioutil"
Fumitoshi Ukai87586e52015-06-25 17:49:35 +090021 "strings"
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090022 "time"
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +090023
24 "github.com/golang/glog"
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090025)
26
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090027// DepGraph represents rules defined in makefiles.
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090028type DepGraph struct {
Fumitoshi Ukai0af44522015-06-25 15:26:08 +090029 nodes []*DepNode
30 vars Vars
31 accessedMks []*accessedMakefile
32 exports map[string]bool
Fumitoshi Ukai09fcd522015-07-15 14:31:50 +090033 vpaths searchPaths
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090034}
35
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090036// Nodes returns all rules.
37func (g *DepGraph) Nodes() []*DepNode { return g.nodes }
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090038
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090039// Vars returns all variables.
40func (g *DepGraph) Vars() Vars { return g.vars }
41
Fumitoshi Ukai8a1110d2015-07-29 13:18:40 +090042func (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 Ukai65c72332015-06-26 21:32:50 +090071// LoadReq is a request to load makefile.
Fumitoshi Ukai87586e52015-06-25 17:49:35 +090072type LoadReq struct {
Fumitoshi Ukai5c850402015-06-26 09:52:55 +090073 Makefile string
74 Targets []string
75 CommandLineVars []string
76 EnvironmentVars []string
77 UseCache bool
78 EagerEvalCommand bool
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090079}
80
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090081// FromCommandLine creates LoadReq from given command line.
Fumitoshi Ukai87586e52015-06-25 17:49:35 +090082func 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 Ukai65c72332015-06-26 21:32:50 +090092 mk, err := defaultMakefile()
93 if err != nil {
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +090094 glog.Warningf("default makefile: %v", err)
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090095 }
Fumitoshi Ukai87586e52015-06-25 17:49:35 +090096 return LoadReq{
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090097 Makefile: mk,
Fumitoshi Ukai87586e52015-06-25 17:49:35 +090098 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)
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +0900106 glog.V(1).Infof("%s var %q", origin, v)
Fumitoshi Ukai87586e52015-06-25 17:49:35 +0900107 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 Ukai65c72332015-06-26 21:32:50 +0900118// Load loads makefile.
Fumitoshi Ukai87586e52015-06-25 17:49:35 +0900119func Load(req LoadReq) (*DepGraph, error) {
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900120 startTime := time.Now()
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900121 var err error
Fumitoshi Ukai87586e52015-06-25 17:49:35 +0900122 if req.Makefile == "" {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900123 req.Makefile, err = defaultMakefile()
124 if err != nil {
125 return nil, err
126 }
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900127 }
128
Fumitoshi Ukai87586e52015-06-25 17:49:35 +0900129 if req.UseCache {
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900130 g, err := loadCache(req.Makefile, req.Targets)
131 if err == nil {
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900132 return g, nil
133 }
134 }
135
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900136 bmk, err := bootstrapMakefile(req.Targets)
137 if err != nil {
138 return nil, err
139 }
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900140
Fumitoshi Ukai87586e52015-06-25 17:49:35 +0900141 content, err := ioutil.ReadFile(req.Makefile)
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900142 if err != nil {
143 return nil, err
144 }
Fumitoshi Ukai87586e52015-06-25 17:49:35 +0900145 mk, err := parseMakefile(content, req.Makefile)
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900146 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 Ukai87586e52015-06-25 17:49:35 +0900157 err = initVars(vars, req.EnvironmentVars, "environment")
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900158 if err != nil {
159 return nil, err
160 }
Fumitoshi Ukai87586e52015-06-25 17:49:35 +0900161 err = initVars(vars, req.CommandLineVars, "command line")
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900162 if err != nil {
163 return nil, err
164 }
Fumitoshi Ukai87586e52015-06-25 17:49:35 +0900165 er, err := eval(mk, vars, req.UseCache)
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900166 if err != nil {
167 return nil, err
168 }
169 vars.Merge(er.vars)
170
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900171 logStats("eval time: %q", time.Since(startTime))
172 logStats("shell func time: %q %d", shellStats.Duration(), shellStats.Count())
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900173
174 startTime = time.Now()
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900175 db, err := newDepBuilder(er, vars)
176 if err != nil {
177 return nil, err
178 }
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900179 logStats("dep build prepare time: %q", time.Since(startTime))
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900180
181 startTime = time.Now()
Fumitoshi Ukai87586e52015-06-25 17:49:35 +0900182 nodes, err := db.Eval(req.Targets)
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900183 if err != nil {
184 return nil, err
185 }
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900186 logStats("dep build time: %q", time.Since(startTime))
Fumitoshi Ukai0af44522015-06-25 15:26:08 +0900187 var accessedMks []*accessedMakefile
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900188 // Always put the root Makefile as the first element.
Fumitoshi Ukai0af44522015-06-25 15:26:08 +0900189 accessedMks = append(accessedMks, &accessedMakefile{
Fumitoshi Ukai87586e52015-06-25 17:49:35 +0900190 Filename: req.Makefile,
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900191 Hash: sha1.Sum(content),
Fumitoshi Ukai0af44522015-06-25 15:26:08 +0900192 State: fileExists,
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900193 })
Fumitoshi Ukai0af44522015-06-25 15:26:08 +0900194 accessedMks = append(accessedMks, er.accessedMks...)
Fumitoshi Ukai5c850402015-06-26 09:52:55 +0900195 gd := &DepGraph{
Fumitoshi Ukai0af44522015-06-25 15:26:08 +0900196 nodes: nodes,
197 vars: vars,
198 accessedMks: accessedMks,
199 exports: er.exports,
Fumitoshi Ukai09fcd522015-07-15 14:31:50 +0900200 vpaths: er.vpaths,
Fumitoshi Ukai5c850402015-06-26 09:52:55 +0900201 }
202 if req.EagerEvalCommand {
203 startTime := time.Now()
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900204 err = evalCommands(nodes, vars)
205 if err != nil {
206 return nil, err
207 }
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900208 logStats("eager eval command time: %q", time.Since(startTime))
Fumitoshi Ukai5c850402015-06-26 09:52:55 +0900209 }
210 if req.UseCache {
211 startTime := time.Now()
212 saveCache(gd, req.Targets)
Fumitoshi Ukai49599e52015-06-26 10:10:24 +0900213 logStats("serialize time: %q", time.Since(startTime))
Fumitoshi Ukai5c850402015-06-26 09:52:55 +0900214 }
215 return gd, nil
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900216}
Fumitoshi Ukai4bb4cd52015-06-25 18:07:21 +0900217
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900218// Loader is the interface that loads DepGraph.
Fumitoshi Ukai4bb4cd52015-06-25 18:07:21 +0900219type Loader interface {
220 Load(string) (*DepGraph, error)
221}
222
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900223// Saver is the interface that saves DepGraph.
Fumitoshi Ukai4bb4cd52015-06-25 18:07:21 +0900224type Saver interface {
225 Save(*DepGraph, string, []string) error
226}
227
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900228// LoadSaver is the interface that groups Load and Save methods.
Fumitoshi Ukai4bb4cd52015-06-25 18:07:21 +0900229type LoadSaver interface {
230 Loader
231 Saver
232}