• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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