This commit is contained in:
Daniel Jones 2013-03-03 17:51:00 -08:00
parent ee70f46012
commit 8a218f35c0
6 changed files with 586 additions and 623 deletions

282
graph.go
View file

@ -1,206 +1,198 @@
package main
import (
"fmt"
"io"
"os"
"sync"
"time"
"fmt"
"io"
"os"
"sync"
"time"
)
// A dependency graph
type graph struct {
root *node // the intial target's node
nodes map[string]*node // map targets to their nodes
root *node // the intial target's node
nodes map[string]*node // map targets to their nodes
}
// An edge in the graph.
type edge struct {
v *node // node this edge directs to
stem string // stem matched for meta-rule applications
matches []string // regular expression matches
r *rule
v *node // node this edge directs to
stem string // stem matched for meta-rule applications
matches []string // regular expression matches
r *rule
}
// Current status of a node in the build.
type nodeStatus int
const (
nodeStatusReady nodeStatus = iota
nodeStatusStarted
nodeStatusDone
nodeStatusFailed
nodeStatusReady nodeStatus = iota
nodeStatusStarted
nodeStatusDone
nodeStatusFailed
)
// A node in the dependency graph
type node struct {
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
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
}
// Create a new node
func (g *graph) newnode(name string) *node {
u := &node{name: name}
info, err := os.Stat(name)
if err == nil {
u.t = info.ModTime()
u.exists = true
} else {
_, ok := err.(*os.PathError)
if ok {
u.exists = false
} else {
mkError(err.Error())
}
}
g.nodes[name] = u
return u
u := &node{name: name}
info, err := os.Stat(name)
if err == nil {
u.t = info.ModTime()
u.exists = true
} else {
_, ok := err.(*os.PathError)
if ok {
u.exists = false
} else {
mkError(err.Error())
}
}
g.nodes[name] = u
return u
}
// Print a graph in graphviz format.
func (g *graph) visualize(w io.Writer) {
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, "}")
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, "}")
}
// Create a new arc.
func (u *node) newedge(v *node, r *rule) *edge {
e := &edge{v: v, r: r}
u.prereqs = append(u.prereqs, e)
return e
e := &edge{v: v, r: r}
u.prereqs = append(u.prereqs, e)
return e
}
// Create a dependency graph for the given target.
func buildgraph(rs *ruleSet, target string) *graph {
g := &graph{nil, make(map[string]*node)}
g := &graph{nil, make(map[string]*node)}
// 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)
// 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)
return g
return g
}
// 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 {
u, ok := g.nodes[target]
if ok {
return u
}
u = g.newnode(target)
u, ok := g.nodes[target]
if ok {
return u
}
u = g.newnode(target)
// does the target match a concrete rule?
// does the target match a concrete rule?
ks, ok := rs.targetrules[target]
if ok {
for ki := range ks {
k := ks[ki]
if rulecnt[k] > max_rule_cnt {
continue
}
ks, ok := rs.targetrules[target]
if ok {
for ki := range ks {
k := ks[ki]
if rulecnt[k] > max_rule_cnt {
continue
}
r := &rs.rules[k]
r := &rs.rules[k]
// skip meta-rules
if r.ismeta {
continue
}
// skip meta-rules
if r.ismeta {
continue
}
// skip rules that have no effect
if r.recipe == "" && len(r.prereqs) == 0 {
continue
}
// skip rules that have no effect
if r.recipe == "" && len(r.prereqs) == 0 {
continue
}
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
}
}
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 {
if rulecnt[k] > max_rule_cnt {
continue
}
// find applicable metarules
for k := range rs.rules {
if rulecnt[k] > max_rule_cnt {
continue
}
r := &rs.rules[k]
r := &rs.rules[k]
if !r.ismeta {
continue
}
if !r.ismeta {
continue
}
// skip rules that have no effect
if r.recipe == "" && len(r.prereqs) == 0 {
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
}
for j := range r.targets {
mat := r.targets[j].match(target)
if mat == nil {
continue
}
var stem string
var matches []string
var stem string
var matches []string
if r.attributes.regex {
matches = mat
} else {
stem = mat[1]
}
if r.attributes.regex {
matches = mat
} 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 {
// TODO: write substituteRegexpRefs and use that here
prereq = r.prereqs[i]
} else {
prereq = expandSuffixes(r.prereqs[i], stem)
}
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 {
// TODO: write substituteRegexpRefs and use that here
prereq = r.prereqs[i]
} 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
}
}
e := u.newedge(applyrules(rs, g, prereq, rulecnt), r)
e.stem = stem
e.matches = matches
}
}
rulecnt[k] -= 1
}
}
return u
return u
}