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	"fmt"
19	"os"
20	"time"
21
22	"github.com/golang/glog"
23)
24
25// Executor manages execution of makefile rules.
26type Executor struct {
27	rules         map[string]*rule
28	implicitRules []*rule
29	suffixRules   map[string][]*rule
30	firstRule     *rule
31	// target -> Job, nil means the target is currently being processed.
32	done map[string]*job
33
34	wm *workerManager
35
36	ctx *execContext
37
38	trace          []string
39	buildCnt       int
40	alreadyDoneCnt int
41	noRuleCnt      int
42	upToDateCnt    int
43	runCommandCnt  int
44}
45
46func (ex *Executor) makeJobs(n *DepNode, neededBy *job) error {
47	output, _ := ex.ctx.vpaths.exists(n.Output)
48	if neededBy != nil {
49		glog.V(1).Infof("MakeJob: %s for %s", output, neededBy.n.Output)
50	}
51	n.Output = output
52	ex.buildCnt++
53	if ex.buildCnt%100 == 0 {
54		ex.reportStats()
55	}
56
57	j, present := ex.done[output]
58
59	if present {
60		if j == nil {
61			if !n.IsPhony {
62				fmt.Printf("Circular %s <- %s dependency dropped.\n", neededBy.n.Output, n.Output)
63			}
64			if neededBy != nil {
65				neededBy.numDeps--
66			}
67		} else {
68			glog.Infof("%s already done: %d", j.n.Output, j.outputTs)
69			if neededBy != nil {
70				ex.wm.ReportNewDep(j, neededBy)
71			}
72		}
73		return nil
74	}
75
76	j = &job{
77		n:       n,
78		ex:      ex,
79		numDeps: len(n.Deps) + len(n.OrderOnlys),
80		depsTs:  int64(-1),
81	}
82	if neededBy != nil {
83		j.parents = append(j.parents, neededBy)
84	}
85
86	ex.done[output] = nil
87	// We iterate n.Deps twice. In the first run, we may modify
88	// numDeps. There will be a race if we do so after the first
89	// ex.makeJobs(d, j).
90	var deps []*DepNode
91	for _, d := range n.Deps {
92		deps = append(deps, d)
93	}
94	for _, d := range n.OrderOnlys {
95		if _, ok := ex.ctx.vpaths.exists(d.Output); ok {
96			j.numDeps--
97			continue
98		}
99		deps = append(deps, d)
100	}
101	glog.V(1).Infof("new: %s (%d)", j.n.Output, j.numDeps)
102
103	for _, d := range deps {
104		ex.trace = append(ex.trace, d.Output)
105		err := ex.makeJobs(d, j)
106		ex.trace = ex.trace[0 : len(ex.trace)-1]
107		if err != nil {
108			return err
109		}
110	}
111
112	ex.done[output] = j
113	return ex.wm.PostJob(j)
114}
115
116func (ex *Executor) reportStats() {
117	if !PeriodicStatsFlag {
118		return
119	}
120
121	logStats("build=%d alreadyDone=%d noRule=%d, upToDate=%d runCommand=%d",
122		ex.buildCnt, ex.alreadyDoneCnt, ex.noRuleCnt, ex.upToDateCnt, ex.runCommandCnt)
123	if len(ex.trace) > 1 {
124		logStats("trace=%q", ex.trace)
125	}
126}
127
128// ExecutorOpt is an option for Executor.
129type ExecutorOpt struct {
130	NumJobs int
131}
132
133// NewExecutor creates new Executor.
134func NewExecutor(opt *ExecutorOpt) (*Executor, error) {
135	if opt == nil {
136		opt = &ExecutorOpt{NumJobs: 1}
137	}
138	if opt.NumJobs < 1 {
139		opt.NumJobs = 1
140	}
141	wm, err := newWorkerManager(opt.NumJobs)
142	if err != nil {
143		return nil, err
144	}
145	ex := &Executor{
146		rules:       make(map[string]*rule),
147		suffixRules: make(map[string][]*rule),
148		done:        make(map[string]*job),
149		wm:          wm,
150	}
151	return ex, nil
152}
153
154// Exec executes to build targets, or first target in DepGraph.
155func (ex *Executor) Exec(g *DepGraph, targets []string) error {
156	ex.ctx = newExecContext(g.vars, g.vpaths, false)
157
158	// TODO: Handle target specific variables.
159	for name, export := range g.exports {
160		if export {
161			v, err := ex.ctx.ev.EvaluateVar(name)
162			if err != nil {
163				return err
164			}
165			os.Setenv(name, v)
166		} else {
167			os.Unsetenv(name)
168		}
169	}
170
171	startTime := time.Now()
172	var nodes []*DepNode
173	if len(targets) == 0 {
174		if len(g.nodes) > 0 {
175			nodes = append(nodes, g.nodes[0])
176		}
177	} else {
178		m := make(map[string]*DepNode)
179		for _, n := range g.nodes {
180			m[n.Output] = n
181		}
182		for _, t := range targets {
183			n := m[t]
184			if n != nil {
185				nodes = append(nodes, n)
186			}
187		}
188	}
189	for _, root := range nodes {
190		err := ex.makeJobs(root, nil)
191		if err != nil {
192			break
193		}
194	}
195	n, err := ex.wm.Wait()
196	logStats("exec time: %q", time.Since(startTime))
197	if n == 0 {
198		for _, root := range nodes {
199			fmt.Printf("kati: Nothing to be done for `%s'.\n", root.Output)
200		}
201	}
202	return err
203}
204