2013-03-03 17:50:00 -08:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
2013-03-03 17:51:00 -08:00
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"os"
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
2013-03-03 17:50:00 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// A dependency graph
|
|
|
|
|
type graph struct {
|
2013-03-03 17:51:00 -08:00
|
|
|
root *node // the intial target's node
|
|
|
|
|
nodes map[string]*node // map targets to their nodes
|
2013-03-03 17:50:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// An edge in the graph.
|
|
|
|
|
type edge struct {
|
2013-03-03 17:51:00 -08:00
|
|
|
v *node // node this edge directs to
|
|
|
|
|
stem string // stem matched for meta-rule applications
|
|
|
|
|
matches []string // regular expression matches
|
2013-03-09 19:27:28 -08:00
|
|
|
togo bool // this edge is going to be pruned
|
2013-03-03 17:51:00 -08:00
|
|
|
r *rule
|
2013-03-03 17:50:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Current status of a node in the build.
|
|
|
|
|
type nodeStatus int
|
|
|
|
|
|
|
|
|
|
const (
|
2013-03-03 17:51:00 -08:00
|
|
|
nodeStatusReady nodeStatus = iota
|
|
|
|
|
nodeStatusStarted
|
2013-04-24 11:36:24 -07:00
|
|
|
nodeStatusNop
|
2013-03-03 17:51:00 -08:00
|
|
|
nodeStatusDone
|
|
|
|
|
nodeStatusFailed
|
2013-03-03 17:50:00 -08:00
|
|
|
)
|
|
|
|
|
|
2013-03-09 19:27:28 -08:00
|
|
|
type nodeFlag int
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
nodeFlagCycle nodeFlag = 0x0002
|
|
|
|
|
nodeFlagReady = 0x0004
|
|
|
|
|
nodeFlagProbable = 0x0100
|
|
|
|
|
nodeFlagVacuous = 0x0200
|
|
|
|
|
)
|
|
|
|
|
|
2013-03-03 17:50:00 -08:00
|
|
|
// A node in the dependency graph
|
|
|
|
|
type node struct {
|
2013-03-03 17:51:00 -08:00
|
|
|
r *rule // rule to be applied
|
|
|
|
|
name string // target name
|
|
|
|
|
prog string // custom program to compare times
|
|
|
|
|
t time.Time // file modification time
|
|
|
|
|
exists bool // does a non-virtual target exist
|
|
|
|
|
prereqs []*edge // prerequisite rules
|
|
|
|
|
status nodeStatus // current state of the node in the build
|
|
|
|
|
mutex sync.Mutex // exclusivity for the status variable
|
|
|
|
|
listeners []chan nodeStatus // channels to notify of completion
|
2013-03-09 19:27:28 -08:00
|
|
|
flags nodeFlag // bitwise combination of node flags
|
2013-03-03 17:50:00 -08:00
|
|
|
}
|
|
|
|
|
|
2013-03-09 20:54:13 -08:00
|
|
|
// Update a node's timestamp and 'exists' flag.
|
|
|
|
|
func (u *node) updateTimestamp() {
|
|
|
|
|
info, err := os.Stat(u.name)
|
2013-03-03 17:51:00 -08:00
|
|
|
if err == nil {
|
|
|
|
|
u.t = info.ModTime()
|
|
|
|
|
u.exists = true
|
2013-03-09 19:27:28 -08:00
|
|
|
u.flags |= nodeFlagProbable
|
2013-03-03 17:51:00 -08:00
|
|
|
} else {
|
|
|
|
|
_, ok := err.(*os.PathError)
|
|
|
|
|
if ok {
|
2013-03-10 00:34:42 -08:00
|
|
|
u.t = time.Unix(0, 0)
|
2013-03-03 17:51:00 -08:00
|
|
|
u.exists = false
|
|
|
|
|
} else {
|
|
|
|
|
mkError(err.Error())
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-03-18 20:37:01 -07:00
|
|
|
|
|
|
|
|
if rebuildall {
|
|
|
|
|
u.flags |= nodeFlagProbable
|
|
|
|
|
}
|
2013-03-09 20:54:13 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a new node
|
|
|
|
|
func (g *graph) newnode(name string) *node {
|
|
|
|
|
u := &node{name: name}
|
2013-03-10 00:34:42 -08:00
|
|
|
u.updateTimestamp()
|
2013-03-03 17:51:00 -08:00
|
|
|
g.nodes[name] = u
|
|
|
|
|
return u
|
2013-03-03 17:50:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Print a graph in graphviz format.
|
|
|
|
|
func (g *graph) visualize(w io.Writer) {
|
2013-03-03 17:51:00 -08:00
|
|
|
fmt.Fprintln(w, "digraph mk {")
|
|
|
|
|
for t, u := range g.nodes {
|
|
|
|
|
for i := range u.prereqs {
|
|
|
|
|
if u.prereqs[i].v != nil {
|
|
|
|
|
fmt.Fprintf(w, " \"%s\" -> \"%s\";\n", t, u.prereqs[i].v.name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintln(w, "}")
|
2013-03-03 17:50:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a new arc.
|
|
|
|
|
func (u *node) newedge(v *node, r *rule) *edge {
|
2013-03-03 17:51:00 -08:00
|
|
|
e := &edge{v: v, r: r}
|
|
|
|
|
u.prereqs = append(u.prereqs, e)
|
|
|
|
|
return e
|
2013-03-03 17:50:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a dependency graph for the given target.
|
|
|
|
|
func buildgraph(rs *ruleSet, target string) *graph {
|
2013-03-03 17:51:00 -08:00
|
|
|
g := &graph{nil, make(map[string]*node)}
|
2013-03-03 17:50:00 -08:00
|
|
|
|
2013-03-03 17:51:00 -08:00
|
|
|
// keep track of how many times each rule is visited, to avoid cycles.
|
|
|
|
|
rulecnt := make([]int, len(rs.rules))
|
|
|
|
|
g.root = applyrules(rs, g, target, rulecnt)
|
2013-03-10 00:34:42 -08:00
|
|
|
g.cyclecheck(g.root)
|
2013-03-09 19:27:28 -08:00
|
|
|
g.root.flags |= nodeFlagProbable
|
2013-03-10 00:34:42 -08:00
|
|
|
g.vacuous(g.root)
|
|
|
|
|
g.ambiguous(g.root)
|
2013-03-03 17:50:00 -08:00
|
|
|
|
2013-03-03 17:51:00 -08:00
|
|
|
return g
|
2013-03-03 17:50:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Recursively match the given target to a rule in the rule set to construct the
|
|
|
|
|
// full graph.
|
|
|
|
|
func applyrules(rs *ruleSet, g *graph, target string, rulecnt []int) *node {
|
2013-03-03 17:51:00 -08:00
|
|
|
u, ok := g.nodes[target]
|
|
|
|
|
if ok {
|
|
|
|
|
return u
|
|
|
|
|
}
|
|
|
|
|
u = g.newnode(target)
|
|
|
|
|
|
|
|
|
|
// does the target match a concrete rule?
|
|
|
|
|
|
|
|
|
|
ks, ok := rs.targetrules[target]
|
|
|
|
|
if ok {
|
|
|
|
|
for ki := range ks {
|
|
|
|
|
k := ks[ki]
|
2013-03-04 00:06:24 -08:00
|
|
|
if rulecnt[k] > maxRuleCnt {
|
2013-03-03 17:51:00 -08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r := &rs.rules[k]
|
|
|
|
|
|
|
|
|
|
// skip meta-rules
|
|
|
|
|
if r.ismeta {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// skip rules that have no effect
|
|
|
|
|
if r.recipe == "" && len(r.prereqs) == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-09 19:27:28 -08:00
|
|
|
u.flags |= nodeFlagProbable
|
2013-03-03 17:51:00 -08:00
|
|
|
rulecnt[k] += 1
|
|
|
|
|
if len(r.prereqs) == 0 {
|
|
|
|
|
u.newedge(nil, r)
|
|
|
|
|
} else {
|
|
|
|
|
for i := range r.prereqs {
|
|
|
|
|
u.newedge(applyrules(rs, g, r.prereqs[i], rulecnt), r)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rulecnt[k] -= 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find applicable metarules
|
|
|
|
|
for k := range rs.rules {
|
2013-03-09 19:27:28 -08:00
|
|
|
if rulecnt[k] >= maxRuleCnt {
|
2013-03-03 17:51:00 -08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r := &rs.rules[k]
|
|
|
|
|
|
|
|
|
|
if !r.ismeta {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// skip rules that have no effect
|
|
|
|
|
if r.recipe == "" && len(r.prereqs) == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for j := range r.targets {
|
|
|
|
|
mat := r.targets[j].match(target)
|
|
|
|
|
if mat == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var stem string
|
|
|
|
|
var matches []string
|
2014-02-01 19:03:11 -08:00
|
|
|
var match_vars = make(map[string][]string)
|
2013-03-03 17:51:00 -08:00
|
|
|
|
|
|
|
|
if r.attributes.regex {
|
|
|
|
|
matches = mat
|
2014-02-01 19:03:11 -08:00
|
|
|
for i := range matches {
|
|
|
|
|
key := fmt.Sprintf("stem%d", i)
|
|
|
|
|
match_vars[key] = matches[i : i+1]
|
|
|
|
|
}
|
2013-03-03 17:51:00 -08:00
|
|
|
} else {
|
|
|
|
|
stem = mat[1]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rulecnt[k] += 1
|
|
|
|
|
if len(r.prereqs) == 0 {
|
|
|
|
|
e := u.newedge(nil, r)
|
|
|
|
|
e.stem = stem
|
|
|
|
|
e.matches = matches
|
|
|
|
|
} else {
|
|
|
|
|
for i := range r.prereqs {
|
|
|
|
|
var prereq string
|
|
|
|
|
if r.attributes.regex {
|
2014-02-01 19:03:11 -08:00
|
|
|
prereq = expandRecipeSigils(r.prereqs[i], match_vars)
|
2013-03-03 17:51:00 -08:00
|
|
|
} else {
|
|
|
|
|
prereq = expandSuffixes(r.prereqs[i], stem)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e := u.newedge(applyrules(rs, g, prereq, rulecnt), r)
|
|
|
|
|
e.stem = stem
|
|
|
|
|
e.matches = matches
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rulecnt[k] -= 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return u
|
2013-03-03 17:50:00 -08:00
|
|
|
}
|
2013-03-09 19:27:28 -08:00
|
|
|
|
|
|
|
|
// Remove edges marked as togo.
|
|
|
|
|
func (g *graph) togo(u *node) {
|
|
|
|
|
n := 0
|
|
|
|
|
for i := range u.prereqs {
|
|
|
|
|
if !u.prereqs[i].togo {
|
|
|
|
|
n++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
prereqs := make([]*edge, n)
|
|
|
|
|
j := 0
|
|
|
|
|
for i := range u.prereqs {
|
|
|
|
|
if !u.prereqs[i].togo {
|
|
|
|
|
prereqs[j] = u.prereqs[i]
|
|
|
|
|
j++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: We may have to delete nodes from g.nodes, right?
|
|
|
|
|
|
|
|
|
|
u.prereqs = prereqs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove vacous children of n.
|
|
|
|
|
func (g *graph) vacuous(u *node) bool {
|
|
|
|
|
vac := u.flags&nodeFlagProbable == 0
|
|
|
|
|
if u.flags&nodeFlagReady != 0 {
|
|
|
|
|
return vac
|
|
|
|
|
}
|
|
|
|
|
u.flags |= nodeFlagReady
|
|
|
|
|
|
|
|
|
|
for i := range u.prereqs {
|
|
|
|
|
e := u.prereqs[i]
|
|
|
|
|
if e.v != nil && g.vacuous(e.v) && e.r.ismeta {
|
|
|
|
|
e.togo = true
|
|
|
|
|
} else {
|
|
|
|
|
vac = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if a rule generated edges that are not togo, keep all of its edges
|
|
|
|
|
for i := range u.prereqs {
|
|
|
|
|
e := u.prereqs[i]
|
|
|
|
|
if !e.togo {
|
|
|
|
|
for j := range u.prereqs {
|
|
|
|
|
f := u.prereqs[j]
|
|
|
|
|
if e.r == f.r {
|
|
|
|
|
f.togo = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g.togo(u)
|
|
|
|
|
if vac {
|
|
|
|
|
u.flags |= nodeFlagVacuous
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return vac
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for cycles
|
|
|
|
|
func (g *graph) cyclecheck(u *node) {
|
2013-03-10 00:34:42 -08:00
|
|
|
if u.flags&nodeFlagCycle != 0 && len(u.prereqs) > 0 {
|
|
|
|
|
mkError(fmt.Sprintf("cycle in the graph detected at target %s", u.name))
|
|
|
|
|
}
|
|
|
|
|
u.flags |= nodeFlagCycle
|
|
|
|
|
for i := range u.prereqs {
|
|
|
|
|
if u.prereqs[i].v != nil {
|
|
|
|
|
g.cyclecheck(u.prereqs[i].v)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
u.flags &= ^nodeFlagCycle
|
2013-03-09 19:27:28 -08:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Deal with ambiguous rules.
|
|
|
|
|
func (g *graph) ambiguous(u *node) {
|
|
|
|
|
bad := 0
|
|
|
|
|
var le *edge
|
|
|
|
|
for i := range u.prereqs {
|
|
|
|
|
e := u.prereqs[i]
|
|
|
|
|
|
|
|
|
|
if e.v != nil {
|
|
|
|
|
g.ambiguous(e.v)
|
|
|
|
|
}
|
|
|
|
|
if e.r.recipe == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if le == nil || le.r == nil {
|
|
|
|
|
le = e
|
|
|
|
|
} else {
|
2013-03-29 22:19:10 -07:00
|
|
|
if !le.r.equivRecipe(e.r) {
|
2013-03-09 19:27:28 -08:00
|
|
|
if le.r.ismeta && !e.r.ismeta {
|
2015-03-24 17:47:02 -07:00
|
|
|
mkPrintRecipe(u.name, le.r.recipe, false)
|
2013-03-09 19:27:28 -08:00
|
|
|
le.togo = true
|
|
|
|
|
le = e
|
|
|
|
|
} else if !le.r.ismeta && e.r.ismeta {
|
2015-03-24 17:47:02 -07:00
|
|
|
mkPrintRecipe(u.name, e.r.recipe, false)
|
2013-03-09 19:27:28 -08:00
|
|
|
e.togo = true
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-03-29 22:19:10 -07:00
|
|
|
if !le.r.equivRecipe(e.r) {
|
2013-03-09 19:27:28 -08:00
|
|
|
if bad == 0 {
|
2013-03-29 22:19:10 -07:00
|
|
|
mkPrintError(fmt.Sprintf("mk: ambiguous recipes for %s\n", u.name))
|
2013-03-09 19:27:28 -08:00
|
|
|
bad = 1
|
|
|
|
|
g.trace(u.name, le)
|
|
|
|
|
}
|
|
|
|
|
g.trace(u.name, e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if bad > 0 {
|
|
|
|
|
mkError("")
|
|
|
|
|
}
|
|
|
|
|
g.togo(u)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Print a trace of rules, k
|
|
|
|
|
func (g *graph) trace(name string, e *edge) {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "\t%s", name)
|
|
|
|
|
for true {
|
|
|
|
|
prereqname := ""
|
|
|
|
|
if e.v != nil {
|
|
|
|
|
prereqname = e.v.name
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(os.Stderr, " <-(%s:%d)- %s", e.r.file, e.r.line, prereqname)
|
|
|
|
|
if e.v != nil {
|
|
|
|
|
for i := range e.v.prereqs {
|
|
|
|
|
if e.v.prereqs[i].r.recipe != "" {
|
|
|
|
|
e = e.v.prereqs[i]
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|