go fmt
This commit is contained in:
parent
ee70f46012
commit
8a218f35c0
6 changed files with 586 additions and 623 deletions
329
expand.go
329
expand.go
|
|
@ -1,33 +1,31 @@
|
||||||
|
|
||||||
// String substitution and expansion.
|
// String substitution and expansion.
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// Expand a word. This includes substituting variables and handling quotes.
|
// Expand a word. This includes substituting variables and handling quotes.
|
||||||
func expand(input string, vars map[string][]string, expandBackticks bool) []string {
|
func expand(input string, vars map[string][]string, expandBackticks bool) []string {
|
||||||
parts := make([]string, 0)
|
parts := make([]string, 0)
|
||||||
expanded := ""
|
expanded := ""
|
||||||
var i, j int
|
var i, j int
|
||||||
for i = 0; i < len(input); {
|
for i = 0; i < len(input); {
|
||||||
j = i + strings.IndexAny(input[i:], "\"'`$\\")
|
j = i + strings.IndexAny(input[i:], "\"'`$\\")
|
||||||
|
|
||||||
if j < 0 {
|
if j < 0 {
|
||||||
expanded += input[i:]
|
expanded += input[i:]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
println("-------------------")
|
println("-------------------")
|
||||||
println(len(input))
|
println(len(input))
|
||||||
println(i)
|
println(i)
|
||||||
println(j)
|
println(j)
|
||||||
|
|
||||||
expanded += input[i:j]
|
expanded += input[i:j]
|
||||||
c, w := utf8.DecodeRuneInString(input[j:])
|
c, w := utf8.DecodeRuneInString(input[j:])
|
||||||
i = j + w
|
i = j + w
|
||||||
|
|
||||||
|
|
@ -36,41 +34,41 @@ func expand(input string, vars map[string][]string, expandBackticks bool) []stri
|
||||||
switch c {
|
switch c {
|
||||||
case '\\':
|
case '\\':
|
||||||
out, off = expandEscape(input[i:])
|
out, off = expandEscape(input[i:])
|
||||||
expanded += out
|
expanded += out
|
||||||
|
|
||||||
case '"':
|
case '"':
|
||||||
out, off = expandDoubleQuoted(input[i:], vars, expandBackticks)
|
out, off = expandDoubleQuoted(input[i:], vars, expandBackticks)
|
||||||
expanded += out
|
expanded += out
|
||||||
|
|
||||||
case '\'':
|
case '\'':
|
||||||
out, off = expandSingleQuoted(input[i:])
|
out, off = expandSingleQuoted(input[i:])
|
||||||
expanded += out
|
expanded += out
|
||||||
|
|
||||||
case '`':
|
case '`':
|
||||||
if expandBackticks {
|
if expandBackticks {
|
||||||
out, off = expandBackQuoted(input[i:], vars)
|
out, off = expandBackQuoted(input[i:], vars)
|
||||||
} else {
|
} else {
|
||||||
out = input
|
out = input
|
||||||
off = len(input)
|
off = len(input)
|
||||||
}
|
}
|
||||||
expanded += out
|
expanded += out
|
||||||
|
|
||||||
case '$':
|
case '$':
|
||||||
var outparts []string
|
var outparts []string
|
||||||
outparts, off = expandSigil(input[i:], vars)
|
outparts, off = expandSigil(input[i:], vars)
|
||||||
if len(outparts) > 0 {
|
if len(outparts) > 0 {
|
||||||
outparts[0] = expanded + outparts[0]
|
outparts[0] = expanded + outparts[0]
|
||||||
expanded = outparts[len(outparts)-1]
|
expanded = outparts[len(outparts)-1]
|
||||||
parts = append(parts, outparts[:len(outparts)-1]...)
|
parts = append(parts, outparts[:len(outparts)-1]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i += off
|
i += off
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(expanded) > 0 {
|
if len(expanded) > 0 {
|
||||||
parts = append(parts, expanded)
|
parts = append(parts, expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
return parts
|
return parts
|
||||||
}
|
}
|
||||||
|
|
@ -126,188 +124,177 @@ func expandSingleQuoted(input string) (string, int) {
|
||||||
|
|
||||||
// Expand something starting with at '$'.
|
// Expand something starting with at '$'.
|
||||||
func expandSigil(input string, vars map[string][]string) ([]string, int) {
|
func expandSigil(input string, vars map[string][]string) ([]string, int) {
|
||||||
c, w := utf8.DecodeRuneInString(input)
|
c, w := utf8.DecodeRuneInString(input)
|
||||||
var offset int
|
var offset int
|
||||||
var varname string
|
var varname string
|
||||||
if c == '{' {
|
if c == '{' {
|
||||||
j := strings.IndexRune(input[w:], '}')
|
j := strings.IndexRune(input[w:], '}')
|
||||||
if j < 0 {
|
if j < 0 {
|
||||||
return []string{"$" + input}, len(input)
|
return []string{"$" + input}, len(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
varname = input[w:j]
|
varname = input[w:j]
|
||||||
offset = j + 1
|
offset = j + 1
|
||||||
} else {
|
} else {
|
||||||
// try to match a variable name
|
// try to match a variable name
|
||||||
i := 0
|
i := 0
|
||||||
j := i
|
j := i
|
||||||
for j < len(input) {
|
for j < len(input) {
|
||||||
c, w = utf8.DecodeRuneInString(input[j:])
|
c, w = utf8.DecodeRuneInString(input[j:])
|
||||||
if !(isalpha(c) || c == '_' || (j > i && isdigit(c))) {
|
if !(isalpha(c) || c == '_' || (j > i && isdigit(c))) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
j += w
|
j += w
|
||||||
}
|
}
|
||||||
|
|
||||||
if j > i {
|
if j > i {
|
||||||
varname = input[i:j]
|
varname = input[i:j]
|
||||||
offset = j
|
offset = j
|
||||||
} else {
|
} else {
|
||||||
return []string{"$" + input}, len(input)
|
return []string{"$" + input}, len(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isValidVarName(varname) {
|
if isValidVarName(varname) {
|
||||||
varvals, ok := vars[varname]
|
varvals, ok := vars[varname]
|
||||||
if ok {
|
if ok {
|
||||||
return varvals, offset
|
return varvals, offset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return []string{"$" + input}, len(input)
|
return []string{"$" + input}, len(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Find and expand all sigils.
|
// Find and expand all sigils.
|
||||||
func expandSigils(input string, vars map[string][]string) []string {
|
func expandSigils(input string, vars map[string][]string) []string {
|
||||||
parts := make([]string, 0)
|
parts := make([]string, 0)
|
||||||
expanded := ""
|
expanded := ""
|
||||||
for i := 0; i < len(input); {
|
for i := 0; i < len(input); {
|
||||||
j := strings.IndexRune(input[i:], '$')
|
j := strings.IndexRune(input[i:], '$')
|
||||||
if j < 0 {
|
if j < 0 {
|
||||||
expanded += input[i:]
|
expanded += input[i:]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
ex, k := expandSigil(input[j+1:], vars)
|
ex, k := expandSigil(input[j+1:], vars)
|
||||||
if len(ex) > 0 {
|
if len(ex) > 0 {
|
||||||
ex[0] = expanded + ex[0]
|
ex[0] = expanded + ex[0]
|
||||||
expanded = ex[len(ex)-1]
|
expanded = ex[len(ex)-1]
|
||||||
parts = append(parts, ex[:len(ex)-1]...)
|
parts = append(parts, ex[:len(ex)-1]...)
|
||||||
}
|
}
|
||||||
i = k
|
i = k
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(expanded) > 0 {
|
if len(expanded) > 0 {
|
||||||
parts = append(parts, expanded)
|
parts = append(parts, expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
return parts
|
return parts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Find and expand all sigils in a recipe, producing a flat string.
|
// Find and expand all sigils in a recipe, producing a flat string.
|
||||||
func expandRecipeSigils(input string, vars map[string][]string) string {
|
func expandRecipeSigils(input string, vars map[string][]string) string {
|
||||||
expanded := ""
|
expanded := ""
|
||||||
for i := 0; i < len(input); {
|
for i := 0; i < len(input); {
|
||||||
j := strings.IndexAny(input[i:], "$\\")
|
j := strings.IndexAny(input[i:], "$\\")
|
||||||
if j < 0 {
|
if j < 0 {
|
||||||
expanded += input[i:]
|
expanded += input[i:]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
expanded += input[i:j]
|
expanded += input[i:j]
|
||||||
i = j
|
i = j
|
||||||
c, w := utf8.DecodeRuneInString(input[i:])
|
c, w := utf8.DecodeRuneInString(input[i:])
|
||||||
if c == '$' {
|
if c == '$' {
|
||||||
i += w
|
i += w
|
||||||
ex, k := expandSigil(input[i:], vars)
|
ex, k := expandSigil(input[i:], vars)
|
||||||
expanded += strings.Join(ex, " ")
|
expanded += strings.Join(ex, " ")
|
||||||
i += k
|
i += k
|
||||||
} else if c == '\\' {
|
} else if c == '\\' {
|
||||||
i += w
|
i += w
|
||||||
c, w := utf8.DecodeRuneInString(input[i:])
|
c, w := utf8.DecodeRuneInString(input[i:])
|
||||||
if c == '$' {
|
if c == '$' {
|
||||||
expanded += "$"
|
expanded += "$"
|
||||||
} else {
|
} else {
|
||||||
expanded += "\\" + string(c)
|
expanded += "\\" + string(c)
|
||||||
}
|
}
|
||||||
i += w
|
i += w
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return expanded
|
return expanded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Expand all unescaped '%' characters.
|
// Expand all unescaped '%' characters.
|
||||||
func expandSuffixes(input string, stem string) string {
|
func expandSuffixes(input string, stem string) string {
|
||||||
expanded := make([]byte, 0)
|
expanded := make([]byte, 0)
|
||||||
for i := 0; i < len(input); {
|
for i := 0; i < len(input); {
|
||||||
j := strings.IndexAny(input[i:], "\\%")
|
j := strings.IndexAny(input[i:], "\\%")
|
||||||
if j < 0 {
|
if j < 0 {
|
||||||
expanded = append(expanded, input[i:]...)
|
expanded = append(expanded, input[i:]...)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
c, w := utf8.DecodeRuneInString(input[j:])
|
c, w := utf8.DecodeRuneInString(input[j:])
|
||||||
if c == '%' {
|
if c == '%' {
|
||||||
expanded = append(expanded, stem...)
|
expanded = append(expanded, stem...)
|
||||||
i += w
|
i += w
|
||||||
} else {
|
} else {
|
||||||
j += w
|
j += w
|
||||||
c, w := utf8.DecodeRuneInString(input[j:])
|
c, w := utf8.DecodeRuneInString(input[j:])
|
||||||
if c == '%' {
|
if c == '%' {
|
||||||
expanded = append(expanded, '%')
|
expanded = append(expanded, '%')
|
||||||
i = j + w
|
i = j + w
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(expanded)
|
return string(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: expand RegexpRefs
|
// TODO: expand RegexpRefs
|
||||||
|
|
||||||
|
|
||||||
// Expand a backtick quoted string, by executing the contents.
|
// Expand a backtick quoted string, by executing the contents.
|
||||||
func expandBackQuoted(input string, vars map[string][]string) (string, int) {
|
func expandBackQuoted(input string, vars map[string][]string) (string, int) {
|
||||||
// TODO: expand sigils?
|
// TODO: expand sigils?
|
||||||
j := strings.Index(input, "`")
|
j := strings.Index(input, "`")
|
||||||
if j < 0 {
|
if j < 0 {
|
||||||
return input, len(input)
|
return input, len(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle errors
|
// TODO: handle errors
|
||||||
output, _ := subprocess("sh", nil, input[:j], false, false, true)
|
output, _ := subprocess("sh", nil, input[:j], false, false, true)
|
||||||
return output, (j + 1)
|
return output, (j + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Split a string on whitespace taking into account escaping and quoting.
|
// Split a string on whitespace taking into account escaping and quoting.
|
||||||
//func splitQuoted(input string) []string {
|
//func splitQuoted(input string) []string {
|
||||||
//parts := make([]string, 0)
|
//parts := make([]string, 0)
|
||||||
//var i, j int
|
//var i, j int
|
||||||
//i = 0
|
//i = 0
|
||||||
//for {
|
//for {
|
||||||
//// skip all unescaped whitespace
|
//// skip all unescaped whitespace
|
||||||
//for i < len(input) {
|
//for i < len(input) {
|
||||||
//c, w := utf8.DecodeRuneInString(input[i:])
|
//c, w := utf8.DecodeRuneInString(input[i:])
|
||||||
//if strings.IndexRune(" \t", c) < 0 {
|
//if strings.IndexRune(" \t", c) < 0 {
|
||||||
//break
|
//break
|
||||||
//}
|
//}
|
||||||
//i += w
|
//i += w
|
||||||
//}
|
|
||||||
|
|
||||||
//if i >= len(input) {
|
|
||||||
//break
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// Ugh. Will this take into account quoting in variables?
|
|
||||||
|
|
||||||
//switch c {
|
|
||||||
//case '"':
|
|
||||||
//case '\'':
|
|
||||||
//default:
|
|
||||||
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
//return parts
|
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
//if i >= len(input) {
|
||||||
|
//break
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// Ugh. Will this take into account quoting in variables?
|
||||||
|
|
||||||
|
//switch c {
|
||||||
|
//case '"':
|
||||||
|
//case '\'':
|
||||||
|
//default:
|
||||||
|
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
//return parts
|
||||||
|
//}
|
||||||
|
|
|
||||||
282
graph.go
282
graph.go
|
|
@ -1,206 +1,198 @@
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A dependency graph
|
// A dependency graph
|
||||||
type graph struct {
|
type graph struct {
|
||||||
root *node // the intial target's node
|
root *node // the intial target's node
|
||||||
nodes map[string]*node // map targets to their nodes
|
nodes map[string]*node // map targets to their nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// An edge in the graph.
|
// An edge in the graph.
|
||||||
type edge struct {
|
type edge struct {
|
||||||
v *node // node this edge directs to
|
v *node // node this edge directs to
|
||||||
stem string // stem matched for meta-rule applications
|
stem string // stem matched for meta-rule applications
|
||||||
matches []string // regular expression matches
|
matches []string // regular expression matches
|
||||||
r *rule
|
r *rule
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Current status of a node in the build.
|
// Current status of a node in the build.
|
||||||
type nodeStatus int
|
type nodeStatus int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
nodeStatusReady nodeStatus = iota
|
nodeStatusReady nodeStatus = iota
|
||||||
nodeStatusStarted
|
nodeStatusStarted
|
||||||
nodeStatusDone
|
nodeStatusDone
|
||||||
nodeStatusFailed
|
nodeStatusFailed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// A node in the dependency graph
|
// A node in the dependency graph
|
||||||
type node struct {
|
type node struct {
|
||||||
r *rule // rule to be applied
|
r *rule // rule to be applied
|
||||||
name string // target name
|
name string // target name
|
||||||
prog string // custom program to compare times
|
prog string // custom program to compare times
|
||||||
t time.Time // file modification time
|
t time.Time // file modification time
|
||||||
exists bool // does a non-virtual target exist
|
exists bool // does a non-virtual target exist
|
||||||
prereqs []*edge // prerequisite rules
|
prereqs []*edge // prerequisite rules
|
||||||
status nodeStatus // current state of the node in the build
|
status nodeStatus // current state of the node in the build
|
||||||
mutex sync.Mutex // exclusivity for the status variable
|
mutex sync.Mutex // exclusivity for the status variable
|
||||||
listeners []chan nodeStatus // channels to notify of completion
|
listeners []chan nodeStatus // channels to notify of completion
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new node
|
// Create a new node
|
||||||
func (g *graph) newnode(name string) *node {
|
func (g *graph) newnode(name string) *node {
|
||||||
u := &node{name: name}
|
u := &node{name: name}
|
||||||
info, err := os.Stat(name)
|
info, err := os.Stat(name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
u.t = info.ModTime()
|
u.t = info.ModTime()
|
||||||
u.exists = true
|
u.exists = true
|
||||||
} else {
|
} else {
|
||||||
_, ok := err.(*os.PathError)
|
_, ok := err.(*os.PathError)
|
||||||
if ok {
|
if ok {
|
||||||
u.exists = false
|
u.exists = false
|
||||||
} else {
|
} else {
|
||||||
mkError(err.Error())
|
mkError(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.nodes[name] = u
|
g.nodes[name] = u
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print a graph in graphviz format.
|
// Print a graph in graphviz format.
|
||||||
func (g *graph) visualize(w io.Writer) {
|
func (g *graph) visualize(w io.Writer) {
|
||||||
fmt.Fprintln(w, "digraph mk {")
|
fmt.Fprintln(w, "digraph mk {")
|
||||||
for t, u := range g.nodes {
|
for t, u := range g.nodes {
|
||||||
for i := range u.prereqs {
|
for i := range u.prereqs {
|
||||||
if u.prereqs[i].v != nil {
|
if u.prereqs[i].v != nil {
|
||||||
fmt.Fprintf(w, " \"%s\" -> \"%s\";\n", t, u.prereqs[i].v.name)
|
fmt.Fprintf(w, " \"%s\" -> \"%s\";\n", t, u.prereqs[i].v.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprintln(w, "}")
|
fmt.Fprintln(w, "}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Create a new arc.
|
// Create a new arc.
|
||||||
func (u *node) newedge(v *node, r *rule) *edge {
|
func (u *node) newedge(v *node, r *rule) *edge {
|
||||||
e := &edge{v: v, r: r}
|
e := &edge{v: v, r: r}
|
||||||
u.prereqs = append(u.prereqs, e)
|
u.prereqs = append(u.prereqs, e)
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Create a dependency graph for the given target.
|
// Create a dependency graph for the given target.
|
||||||
func buildgraph(rs *ruleSet, target string) *graph {
|
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.
|
// keep track of how many times each rule is visited, to avoid cycles.
|
||||||
rulecnt := make([]int, len(rs.rules))
|
rulecnt := make([]int, len(rs.rules))
|
||||||
g.root = applyrules(rs, g, target, rulecnt)
|
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
|
// Recursively match the given target to a rule in the rule set to construct the
|
||||||
// full graph.
|
// full graph.
|
||||||
func applyrules(rs *ruleSet, g *graph, target string, rulecnt []int) *node {
|
func applyrules(rs *ruleSet, g *graph, target string, rulecnt []int) *node {
|
||||||
u, ok := g.nodes[target]
|
u, ok := g.nodes[target]
|
||||||
if ok {
|
if ok {
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
u = g.newnode(target)
|
u = g.newnode(target)
|
||||||
|
|
||||||
// does the target match a concrete rule?
|
// does the target match a concrete rule?
|
||||||
|
|
||||||
ks, ok := rs.targetrules[target]
|
ks, ok := rs.targetrules[target]
|
||||||
if ok {
|
if ok {
|
||||||
for ki := range ks {
|
for ki := range ks {
|
||||||
k := ks[ki]
|
k := ks[ki]
|
||||||
if rulecnt[k] > max_rule_cnt {
|
if rulecnt[k] > max_rule_cnt {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
r := &rs.rules[k]
|
r := &rs.rules[k]
|
||||||
|
|
||||||
// skip meta-rules
|
// skip meta-rules
|
||||||
if r.ismeta {
|
if r.ismeta {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip rules that have no effect
|
// skip rules that have no effect
|
||||||
if r.recipe == "" && len(r.prereqs) == 0 {
|
if r.recipe == "" && len(r.prereqs) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rulecnt[k] += 1
|
rulecnt[k] += 1
|
||||||
if len(r.prereqs) == 0 {
|
if len(r.prereqs) == 0 {
|
||||||
u.newedge(nil, r)
|
u.newedge(nil, r)
|
||||||
} else {
|
} else {
|
||||||
for i := range r.prereqs {
|
for i := range r.prereqs {
|
||||||
u.newedge(applyrules(rs, g, r.prereqs[i], rulecnt), r)
|
u.newedge(applyrules(rs, g, r.prereqs[i], rulecnt), r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rulecnt[k] -= 1
|
rulecnt[k] -= 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find applicable metarules
|
// find applicable metarules
|
||||||
for k := range rs.rules {
|
for k := range rs.rules {
|
||||||
if rulecnt[k] > max_rule_cnt {
|
if rulecnt[k] > max_rule_cnt {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
r := &rs.rules[k]
|
r := &rs.rules[k]
|
||||||
|
|
||||||
if !r.ismeta {
|
if !r.ismeta {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip rules that have no effect
|
// skip rules that have no effect
|
||||||
if r.recipe == "" && len(r.prereqs) == 0 {
|
if r.recipe == "" && len(r.prereqs) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for j := range r.targets {
|
for j := range r.targets {
|
||||||
mat := r.targets[j].match(target)
|
mat := r.targets[j].match(target)
|
||||||
if mat == nil {
|
if mat == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var stem string
|
var stem string
|
||||||
var matches []string
|
var matches []string
|
||||||
|
|
||||||
if r.attributes.regex {
|
if r.attributes.regex {
|
||||||
matches = mat
|
matches = mat
|
||||||
} else {
|
} else {
|
||||||
stem = mat[1]
|
stem = mat[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
rulecnt[k] += 1
|
rulecnt[k] += 1
|
||||||
if len(r.prereqs) == 0 {
|
if len(r.prereqs) == 0 {
|
||||||
e := u.newedge(nil, r)
|
e := u.newedge(nil, r)
|
||||||
e.stem = stem
|
e.stem = stem
|
||||||
e.matches = matches
|
e.matches = matches
|
||||||
} else {
|
} else {
|
||||||
for i := range r.prereqs {
|
for i := range r.prereqs {
|
||||||
var prereq string
|
var prereq string
|
||||||
if r.attributes.regex {
|
if r.attributes.regex {
|
||||||
// TODO: write substituteRegexpRefs and use that here
|
// TODO: write substituteRegexpRefs and use that here
|
||||||
prereq = r.prereqs[i]
|
prereq = r.prereqs[i]
|
||||||
} else {
|
} else {
|
||||||
prereq = expandSuffixes(r.prereqs[i], stem)
|
prereq = expandSuffixes(r.prereqs[i], stem)
|
||||||
}
|
}
|
||||||
|
|
||||||
e := u.newedge(applyrules(rs, g, prereq, rulecnt), r)
|
e := u.newedge(applyrules(rs, g, prereq, rulecnt), r)
|
||||||
e.stem = stem
|
e.stem = stem
|
||||||
e.matches = matches
|
e.matches = matches
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rulecnt[k] -= 1
|
rulecnt[k] -= 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
309
mk.go
309
mk.go
|
|
@ -1,13 +1,12 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// True if messages should be printed without fancy colors.
|
// True if messages should be printed without fancy colors.
|
||||||
var nocolor bool = false
|
var nocolor bool = false
|
||||||
|
|
||||||
|
|
@ -19,20 +18,19 @@ const max_rule_cnt = 3
|
||||||
|
|
||||||
// Ansi color codes.
|
// Ansi color codes.
|
||||||
const (
|
const (
|
||||||
colorDefault string = "\033[0m"
|
colorDefault string = "\033[0m"
|
||||||
colorBlack string = "\033[30m"
|
colorBlack string = "\033[30m"
|
||||||
colorRed string = "\033[31m"
|
colorRed string = "\033[31m"
|
||||||
colorGreen string = "\033[32m"
|
colorGreen string = "\033[32m"
|
||||||
colorYellow string = "\033[33m"
|
colorYellow string = "\033[33m"
|
||||||
colorBlue string = "\033[34m"
|
colorBlue string = "\033[34m"
|
||||||
colorMagenta string = "\033[35m"
|
colorMagenta string = "\033[35m"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func mk(rs *ruleSet, target string, dryrun bool) {
|
func mk(rs *ruleSet, target string, dryrun bool) {
|
||||||
g := buildgraph(rs, target)
|
g := buildgraph(rs, target)
|
||||||
//g.visualize(os.Stdout)
|
//g.visualize(os.Stdout)
|
||||||
mkNode(g, g.root)
|
mkNode(g, g.root)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a target in the graph.
|
// Build a target in the graph.
|
||||||
|
|
@ -45,174 +43,173 @@ func mk(rs *ruleSet, target string, dryrun bool) {
|
||||||
// execute our rule.
|
// execute our rule.
|
||||||
//
|
//
|
||||||
func mkNode(g *graph, u *node) {
|
func mkNode(g *graph, u *node) {
|
||||||
// try to claim on this node
|
// try to claim on this node
|
||||||
u.mutex.Lock()
|
u.mutex.Lock()
|
||||||
if u.status != nodeStatusReady {
|
if u.status != nodeStatusReady {
|
||||||
u.mutex.Unlock()
|
u.mutex.Unlock()
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
u.status = nodeStatusStarted
|
u.status = nodeStatusStarted
|
||||||
}
|
}
|
||||||
u.mutex.Unlock()
|
u.mutex.Unlock()
|
||||||
|
|
||||||
// when finished, notify the listeners
|
// when finished, notify the listeners
|
||||||
finalstatus := nodeStatusDone
|
finalstatus := nodeStatusDone
|
||||||
defer func () {
|
defer func() {
|
||||||
u.mutex.Lock()
|
u.mutex.Lock()
|
||||||
u.status = finalstatus
|
u.status = finalstatus
|
||||||
u.mutex.Unlock()
|
u.mutex.Unlock()
|
||||||
for i := range u.listeners {
|
for i := range u.listeners {
|
||||||
u.listeners[i] <- u.status
|
u.listeners[i] <- u.status
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// there's no fucking rules, dude
|
// there's no fucking rules, dude
|
||||||
if len(u.prereqs) == 0 {
|
if len(u.prereqs) == 0 {
|
||||||
if !u.r.attributes.virtual && !u.exists {
|
if !u.r.attributes.virtual && !u.exists {
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
mkError(fmt.Sprintf("don't know how to make %s in %s", u.name, wd))
|
mkError(fmt.Sprintf("don't know how to make %s in %s", u.name, wd))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// there should otherwise be exactly one edge with an associated rule
|
// there should otherwise be exactly one edge with an associated rule
|
||||||
prereqs := make([]*node, 0)
|
prereqs := make([]*node, 0)
|
||||||
var e *edge = nil
|
var e *edge = nil
|
||||||
for i := range u.prereqs {
|
for i := range u.prereqs {
|
||||||
if u.prereqs[i].r != nil {
|
if u.prereqs[i].r != nil {
|
||||||
e = u.prereqs[i]
|
e = u.prereqs[i]
|
||||||
}
|
}
|
||||||
if u.prereqs[i].v != nil {
|
if u.prereqs[i].v != nil {
|
||||||
prereqs = append(prereqs, u.prereqs[i].v)
|
prereqs = append(prereqs, u.prereqs[i].v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this should have been caught during graph building
|
// this should have been caught during graph building
|
||||||
if e == nil {
|
if e == nil {
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
mkError(fmt.Sprintf("don't know how to make %s in %s", u.name, wd))
|
mkError(fmt.Sprintf("don't know how to make %s in %s", u.name, wd))
|
||||||
}
|
}
|
||||||
|
|
||||||
prereqstat := make(chan nodeStatus)
|
prereqstat := make(chan nodeStatus)
|
||||||
pending := 0
|
pending := 0
|
||||||
|
|
||||||
// build prereqs that need building
|
// build prereqs that need building
|
||||||
e.r.mutex.Lock()
|
e.r.mutex.Lock()
|
||||||
for i := range prereqs {
|
for i := range prereqs {
|
||||||
prereqs[i].mutex.Lock()
|
prereqs[i].mutex.Lock()
|
||||||
// needs to be built?
|
// needs to be built?
|
||||||
if !prereqs[i].exists || e.r.attributes.virtual || (u.exists && u.t.Before(prereqs[i].t)) {
|
if !prereqs[i].exists || e.r.attributes.virtual || (u.exists && u.t.Before(prereqs[i].t)) {
|
||||||
switch prereqs[i].status {
|
switch prereqs[i].status {
|
||||||
case nodeStatusReady:
|
case nodeStatusReady:
|
||||||
go mkNode(g, prereqs[i])
|
go mkNode(g, prereqs[i])
|
||||||
fallthrough
|
fallthrough
|
||||||
case nodeStatusStarted:
|
case nodeStatusStarted:
|
||||||
prereqs[i].listeners = append(prereqs[i].listeners, prereqstat)
|
prereqs[i].listeners = append(prereqs[i].listeners, prereqstat)
|
||||||
pending++
|
pending++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prereqs[i].mutex.Unlock()
|
prereqs[i].mutex.Unlock()
|
||||||
}
|
}
|
||||||
e.r.mutex.Unlock()
|
e.r.mutex.Unlock()
|
||||||
|
|
||||||
// wait until all the prereqs are built
|
// wait until all the prereqs are built
|
||||||
//fmt.Printf("%s: %d\n", u.name, pending)
|
//fmt.Printf("%s: %d\n", u.name, pending)
|
||||||
for pending > 0 {
|
for pending > 0 {
|
||||||
//for i := range prereqs {
|
//for i := range prereqs {
|
||||||
//fmt.Println(prereqs[i].name)
|
//fmt.Println(prereqs[i].name)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
s := <-prereqstat
|
s := <-prereqstat
|
||||||
pending--
|
pending--
|
||||||
if s == nodeStatusFailed {
|
if s == nodeStatusFailed {
|
||||||
finalstatus = nodeStatusFailed
|
finalstatus = nodeStatusFailed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute the recipe, unless the prereqs failed
|
// execute the recipe, unless the prereqs failed
|
||||||
if finalstatus != nodeStatusFailed {
|
if finalstatus != nodeStatusFailed {
|
||||||
mkPrintMessage("mking " + u.name)
|
mkPrintMessage("mking " + u.name)
|
||||||
if !dorecipe(u.name, u, e) {
|
if !dorecipe(u.name, u, e) {
|
||||||
finalstatus = nodeStatusFailed
|
finalstatus = nodeStatusFailed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mkPrintSuccess("finished mking " + u.name)
|
mkPrintSuccess("finished mking " + u.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func mkError(msg string) {
|
func mkError(msg string) {
|
||||||
if !nocolor {
|
if !nocolor {
|
||||||
os.Stderr.WriteString(colorRed)
|
os.Stderr.WriteString(colorRed)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "mk: %s\n", msg)
|
fmt.Fprintf(os.Stderr, "mk: %s\n", msg)
|
||||||
if !nocolor {
|
if !nocolor {
|
||||||
os.Stderr.WriteString(colorDefault)
|
os.Stderr.WriteString(colorDefault)
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func mkPrintSuccess(msg string) {
|
func mkPrintSuccess(msg string) {
|
||||||
if nocolor {
|
if nocolor {
|
||||||
fmt.Println(msg)
|
fmt.Println(msg)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s%s%s\n", colorGreen, msg, colorDefault)
|
fmt.Printf("%s%s%s\n", colorGreen, msg, colorDefault)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mkPrintMessage(msg string) {
|
func mkPrintMessage(msg string) {
|
||||||
if nocolor {
|
if nocolor {
|
||||||
fmt.Println(msg)
|
fmt.Println(msg)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s%s%s\n", colorBlue, msg, colorDefault)
|
fmt.Printf("%s%s%s\n", colorBlue, msg, colorDefault)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mkPrintRecipe(msg string) {
|
func mkPrintRecipe(msg string) {
|
||||||
if !nocolor {
|
if !nocolor {
|
||||||
os.Stdout.WriteString(colorYellow)
|
os.Stdout.WriteString(colorYellow)
|
||||||
}
|
}
|
||||||
printIndented(os.Stdout, msg)
|
printIndented(os.Stdout, msg)
|
||||||
if !nocolor {
|
if !nocolor {
|
||||||
os.Stdout.WriteString(colorDefault)
|
os.Stdout.WriteString(colorDefault)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var mkfilepath string
|
var mkfilepath string
|
||||||
var dryrun bool
|
var dryrun bool
|
||||||
flag.StringVar(&mkfilepath, "f", "mkfile", "use the given file as mkfile")
|
flag.StringVar(&mkfilepath, "f", "mkfile", "use the given file as mkfile")
|
||||||
flag.BoolVar(&dryrun, "n", false, "print commands without actually executing")
|
flag.BoolVar(&dryrun, "n", false, "print commands without actually executing")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
mkfile, err := os.Open(mkfilepath)
|
mkfile, err := os.Open(mkfilepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mkError("no mkfile found")
|
mkError("no mkfile found")
|
||||||
}
|
}
|
||||||
input, _ := ioutil.ReadAll(mkfile)
|
input, _ := ioutil.ReadAll(mkfile)
|
||||||
mkfile.Close()
|
mkfile.Close()
|
||||||
|
|
||||||
rs := parse(string(input), mkfilepath)
|
rs := parse(string(input), mkfilepath)
|
||||||
targets := flag.Args()
|
targets := flag.Args()
|
||||||
|
|
||||||
// build the first non-meta rule in the makefile, if none are given explicitly
|
// build the first non-meta rule in the makefile, if none are given explicitly
|
||||||
for i := range rs.rules {
|
for i := range rs.rules {
|
||||||
if !rs.rules[i].ismeta {
|
if !rs.rules[i].ismeta {
|
||||||
for j := range rs.rules[i].targets {
|
for j := range rs.rules[i].targets {
|
||||||
targets = append(targets, rs.rules[i].targets[j].spat)
|
targets = append(targets, rs.rules[i].targets[j].spat)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(targets) == 0 {
|
if len(targets) == 0 {
|
||||||
fmt.Println("mk: nothing to mk")
|
fmt.Println("mk: nothing to mk")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: For multiple targets, we should add a dummy rule that depends on
|
// TODO: For multiple targets, we should add a dummy rule that depends on
|
||||||
// all let mk handle executing each.
|
// all let mk handle executing each.
|
||||||
for _, target := range targets {
|
for _, target := range targets {
|
||||||
mk(rs, target, dryrun)
|
mk(rs, target, dryrun)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
94
parse.go
94
parse.go
|
|
@ -6,8 +6,8 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type parser struct {
|
type parser struct {
|
||||||
|
|
@ -53,8 +53,8 @@ type parserStateFun func(*parser, token) parserStateFun
|
||||||
// Parse a mkfile, returning a new ruleSet.
|
// Parse a mkfile, returning a new ruleSet.
|
||||||
func parse(input string, name string) *ruleSet {
|
func parse(input string, name string) *ruleSet {
|
||||||
rules := &ruleSet{make(map[string][]string),
|
rules := &ruleSet{make(map[string][]string),
|
||||||
make([]rule, 0),
|
make([]rule, 0),
|
||||||
make(map[string][]int)}
|
make(map[string][]int)}
|
||||||
parseInto(input, name, rules)
|
parseInto(input, name, rules)
|
||||||
return rules
|
return rules
|
||||||
}
|
}
|
||||||
|
|
@ -115,9 +115,9 @@ func parsePipeInclude(p *parser, t token) parserStateFun {
|
||||||
}
|
}
|
||||||
|
|
||||||
output, success := subprocess("sh", args, "", false, false, true)
|
output, success := subprocess("sh", args, "", false, false, true)
|
||||||
if !success {
|
if !success {
|
||||||
p.basicErrorAtToken("subprocess include failed", t)
|
p.basicErrorAtToken("subprocess include failed", t)
|
||||||
}
|
}
|
||||||
|
|
||||||
parseInto(output, fmt.Sprintf("%s:sh", p.name), p.rules)
|
parseInto(output, fmt.Sprintf("%s:sh", p.name), p.rules)
|
||||||
|
|
||||||
|
|
@ -275,8 +275,8 @@ func parseRecipe(p *parser, t token) parserStateFun {
|
||||||
if j < len(p.tokenbuf) {
|
if j < len(p.tokenbuf) {
|
||||||
attribs := make([]string, 0)
|
attribs := make([]string, 0)
|
||||||
for k := i + 1; k < j; k++ {
|
for k := i + 1; k < j; k++ {
|
||||||
exparts := expand(p.tokenbuf[k].val, p.rules.vars, true)
|
exparts := expand(p.tokenbuf[k].val, p.rules.vars, true)
|
||||||
attribs = append(attribs, exparts...)
|
attribs = append(attribs, exparts...)
|
||||||
}
|
}
|
||||||
err := r.parseAttribs(attribs)
|
err := r.parseAttribs(attribs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -284,9 +284,9 @@ func parseRecipe(p *parser, t token) parserStateFun {
|
||||||
p.basicErrorAtToken(msg, p.tokenbuf[i+1])
|
p.basicErrorAtToken(msg, p.tokenbuf[i+1])
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.attributes.regex {
|
if r.attributes.regex {
|
||||||
r.ismeta = true
|
r.ismeta = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
j = i
|
j = i
|
||||||
}
|
}
|
||||||
|
|
@ -294,48 +294,48 @@ func parseRecipe(p *parser, t token) parserStateFun {
|
||||||
// targets
|
// targets
|
||||||
r.targets = make([]pattern, 0)
|
r.targets = make([]pattern, 0)
|
||||||
for k := 0; k < i; k++ {
|
for k := 0; k < i; k++ {
|
||||||
exparts := expand(p.tokenbuf[k].val, p.rules.vars, true)
|
exparts := expand(p.tokenbuf[k].val, p.rules.vars, true)
|
||||||
for i := range exparts {
|
for i := range exparts {
|
||||||
targetstr := exparts[i]
|
targetstr := exparts[i]
|
||||||
r.targets = append(r.targets, pattern{spat: targetstr})
|
r.targets = append(r.targets, pattern{spat: targetstr})
|
||||||
|
|
||||||
if r.attributes.regex {
|
if r.attributes.regex {
|
||||||
rpat, err := regexp.Compile(targetstr)
|
rpat, err := regexp.Compile(targetstr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("invalid regular expression: %q", err)
|
msg := fmt.Sprintf("invalid regular expression: %q", err)
|
||||||
p.basicErrorAtToken(msg, p.tokenbuf[k])
|
p.basicErrorAtToken(msg, p.tokenbuf[k])
|
||||||
}
|
}
|
||||||
r.targets[len(r.targets)-1].rpat = rpat
|
r.targets[len(r.targets)-1].rpat = rpat
|
||||||
} else {
|
} else {
|
||||||
idx := strings.IndexRune(targetstr, '%')
|
idx := strings.IndexRune(targetstr, '%')
|
||||||
if idx >= 0 {
|
if idx >= 0 {
|
||||||
var left, right string
|
var left, right string
|
||||||
if idx > 0 {
|
if idx > 0 {
|
||||||
left = regexp.QuoteMeta(targetstr[:idx])
|
left = regexp.QuoteMeta(targetstr[:idx])
|
||||||
}
|
}
|
||||||
if idx < len(targetstr) - 1 {
|
if idx < len(targetstr)-1 {
|
||||||
right = regexp.QuoteMeta(targetstr[idx+1:])
|
right = regexp.QuoteMeta(targetstr[idx+1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
patstr := fmt.Sprintf("^%s(.*)%s$", left, right)
|
patstr := fmt.Sprintf("^%s(.*)%s$", left, right)
|
||||||
rpat, err := regexp.Compile(patstr)
|
rpat, err := regexp.Compile(patstr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("error compiling suffix rule. This is a bug.", err)
|
msg := fmt.Sprintf("error compiling suffix rule. This is a bug.", err)
|
||||||
p.basicErrorAtToken(msg, p.tokenbuf[k])
|
p.basicErrorAtToken(msg, p.tokenbuf[k])
|
||||||
}
|
}
|
||||||
r.targets[len(r.targets)-1].rpat = rpat
|
r.targets[len(r.targets)-1].rpat = rpat
|
||||||
r.targets[len(r.targets)-1].issuffix = true
|
r.targets[len(r.targets)-1].issuffix = true
|
||||||
r.ismeta = true
|
r.ismeta = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// prereqs
|
// prereqs
|
||||||
r.prereqs = make([]string, 0)
|
r.prereqs = make([]string, 0)
|
||||||
for k := j + 1; k < len(p.tokenbuf); k++ {
|
for k := j + 1; k < len(p.tokenbuf); k++ {
|
||||||
exparts := expand(p.tokenbuf[k].val, p.rules.vars, true)
|
exparts := expand(p.tokenbuf[k].val, p.rules.vars, true)
|
||||||
r.prereqs = append(r.prereqs, exparts...)
|
r.prereqs = append(r.prereqs, exparts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.typ == tokenRecipe {
|
if t.typ == tokenRecipe {
|
||||||
|
|
|
||||||
132
recipe.go
132
recipe.go
|
|
@ -1,19 +1,17 @@
|
||||||
|
|
||||||
// Various function for dealing with recipes.
|
// Various function for dealing with recipes.
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"bufio"
|
"strings"
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// Try to unindent a recipe, so that it begins an column 0. (This is mainly for
|
// Try to unindent a recipe, so that it begins an column 0. (This is mainly for
|
||||||
// recipes in python, or other indentation-significant languages.)
|
// recipes in python, or other indentation-significant languages.)
|
||||||
//func stripIndentation(s string) string {
|
//func stripIndentation(s string) string {
|
||||||
|
|
@ -22,78 +20,76 @@ import (
|
||||||
|
|
||||||
// Indent each line of a recipe.
|
// Indent each line of a recipe.
|
||||||
func printIndented(out io.Writer, s string) {
|
func printIndented(out io.Writer, s string) {
|
||||||
reader := bufio.NewReader(strings.NewReader(s))
|
reader := bufio.NewReader(strings.NewReader(s))
|
||||||
for {
|
for {
|
||||||
line, err := reader.ReadString('\n')
|
line, err := reader.ReadString('\n')
|
||||||
if len(line) > 0 {
|
if len(line) > 0 {
|
||||||
io.WriteString(out, " ")
|
io.WriteString(out, " ")
|
||||||
io.WriteString(out, line)
|
io.WriteString(out, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err != nil) {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute a recipe.
|
// Execute a recipe.
|
||||||
func dorecipe(target string, u *node, e *edge) bool {
|
func dorecipe(target string, u *node, e *edge) bool {
|
||||||
vars := make(map[string][]string)
|
vars := make(map[string][]string)
|
||||||
vars["target"] = []string{target}
|
vars["target"] = []string{target}
|
||||||
if e.r.ismeta {
|
if e.r.ismeta {
|
||||||
if e.r.attributes.regex {
|
if e.r.attributes.regex {
|
||||||
for i := range e.matches {
|
for i := range e.matches {
|
||||||
vars[fmt.Sprintf("stem%d", i)] = e.matches[i:i+1]
|
vars[fmt.Sprintf("stem%d", i)] = e.matches[i : i+1]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vars["stem"] = []string{e.stem}
|
vars["stem"] = []string{e.stem}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: other variables to set
|
// TODO: other variables to set
|
||||||
// alltargets
|
// alltargets
|
||||||
// newprereq
|
// newprereq
|
||||||
|
|
||||||
prereqs := make([]string, 0)
|
prereqs := make([]string, 0)
|
||||||
for i := range u.prereqs {
|
for i := range u.prereqs {
|
||||||
if u.prereqs[i].r == e.r && u.prereqs[i].v != nil {
|
if u.prereqs[i].r == e.r && u.prereqs[i].v != nil {
|
||||||
prereqs = append(prereqs, u.prereqs[i].v.name)
|
prereqs = append(prereqs, u.prereqs[i].v.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vars["prereqs"] = prereqs
|
vars["prereqs"] = prereqs
|
||||||
|
|
||||||
input := expandRecipeSigils(e.r.recipe, vars)
|
input := expandRecipeSigils(e.r.recipe, vars)
|
||||||
sh := "sh"
|
sh := "sh"
|
||||||
args := []string{}
|
args := []string{}
|
||||||
|
|
||||||
if len(e.r.shell) > 0 {
|
if len(e.r.shell) > 0 {
|
||||||
sh = e.r.shell[0]
|
sh = e.r.shell[0]
|
||||||
args = e.r.shell[1:]
|
args = e.r.shell[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
if !e.r.attributes.quiet {
|
if !e.r.attributes.quiet {
|
||||||
mkPrintRecipe(input)
|
mkPrintRecipe(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dryrun {
|
if dryrun {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
_, success := subprocess(
|
_, success := subprocess(
|
||||||
sh,
|
sh,
|
||||||
args,
|
args,
|
||||||
input,
|
input,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
false)
|
false)
|
||||||
|
|
||||||
|
// TODO: update the timestamps of each target
|
||||||
|
|
||||||
// TODO: update the timestamps of each target
|
return success
|
||||||
|
|
||||||
return success
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// A monolithic function for executing subprocesses
|
// A monolithic function for executing subprocesses
|
||||||
func subprocess(program string,
|
func subprocess(program string,
|
||||||
args []string,
|
args []string,
|
||||||
|
|
@ -136,15 +132,15 @@ func subprocess(program string,
|
||||||
} else {
|
} else {
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
}
|
}
|
||||||
success := true
|
success := true
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exiterr, ok := err.(*exec.ExitError)
|
exiterr, ok := err.(*exec.ExitError)
|
||||||
if ok {
|
if ok {
|
||||||
success = exiterr.ProcessState.Success()
|
success = exiterr.ProcessState.Success()
|
||||||
} else {
|
} else {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output, success
|
return output, success
|
||||||
|
|
|
||||||
63
ruleset.go
63
ruleset.go
|
|
@ -6,9 +6,9 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"sync"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
"regexp"
|
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type attribSet struct {
|
type attribSet struct {
|
||||||
|
|
@ -27,30 +27,27 @@ type attribError struct {
|
||||||
found rune
|
found rune
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// target and rereq patterns
|
// target and rereq patterns
|
||||||
type pattern struct {
|
type pattern struct {
|
||||||
issuffix bool // is a suffix '%' rule, so we should define $stem.
|
issuffix bool // is a suffix '%' rule, so we should define $stem.
|
||||||
spat string // simple string pattern
|
spat string // simple string pattern
|
||||||
rpat *regexp.Regexp // non-nil if this is a regexp pattern
|
rpat *regexp.Regexp // non-nil if this is a regexp pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Match a pattern, returning an array of submatches, or nil if it doesn'm
|
// Match a pattern, returning an array of submatches, or nil if it doesn'm
|
||||||
// match.
|
// match.
|
||||||
func (p *pattern) match(target string) []string {
|
func (p *pattern) match(target string) []string {
|
||||||
if p.rpat != nil {
|
if p.rpat != nil {
|
||||||
return p.rpat.FindStringSubmatch(target)
|
return p.rpat.FindStringSubmatch(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
if target == p.spat {
|
if target == p.spat {
|
||||||
return make([]string, 0)
|
return make([]string, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// A single rule.
|
// A single rule.
|
||||||
type rule struct {
|
type rule struct {
|
||||||
targets []pattern // non-empty array of targets
|
targets []pattern // non-empty array of targets
|
||||||
|
|
@ -59,20 +56,18 @@ type rule struct {
|
||||||
shell []string // command used to execute the recipe
|
shell []string // command used to execute the recipe
|
||||||
recipe string // recipe source
|
recipe string // recipe source
|
||||||
command []string // command attribute
|
command []string // command attribute
|
||||||
ismeta bool // is this a meta rule
|
ismeta bool // is this a meta rule
|
||||||
mutex sync.Mutex // prevent the rule from being executed multiple times
|
mutex sync.Mutex // prevent the rule from being executed multiple times
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// A set of rules.
|
// A set of rules.
|
||||||
type ruleSet struct {
|
type ruleSet struct {
|
||||||
vars map[string][]string
|
vars map[string][]string
|
||||||
rules []rule
|
rules []rule
|
||||||
// map a target to an array of indexes into rules
|
// map a target to an array of indexes into rules
|
||||||
targetrules map[string][]int
|
targetrules map[string][]int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Read attributes for an array of strings, updating the rule.
|
// Read attributes for an array of strings, updating the rule.
|
||||||
func (r *rule) parseAttribs(inputs []string) *attribError {
|
func (r *rule) parseAttribs(inputs []string) *attribError {
|
||||||
for i := 0; i < len(inputs); i++ {
|
for i := 0; i < len(inputs); i++ {
|
||||||
|
|
@ -124,17 +119,16 @@ func (r *rule) parseAttribs(inputs []string) *attribError {
|
||||||
|
|
||||||
// Add a rule to the rule set.
|
// Add a rule to the rule set.
|
||||||
func (rs *ruleSet) add(r rule) {
|
func (rs *ruleSet) add(r rule) {
|
||||||
rs.rules = append(rs.rules, r)
|
rs.rules = append(rs.rules, r)
|
||||||
k := len(rs.rules) - 1
|
k := len(rs.rules) - 1
|
||||||
for i := range r.targets {
|
for i := range r.targets {
|
||||||
if r.targets[i].rpat == nil {
|
if r.targets[i].rpat == nil {
|
||||||
rs.targetrules[r.targets[i].spat] =
|
rs.targetrules[r.targets[i].spat] =
|
||||||
append(rs.targetrules[r.targets[i].spat], k)
|
append(rs.targetrules[r.targets[i].spat], k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func isValidVarName(v string) bool {
|
func isValidVarName(v string) bool {
|
||||||
for i := 0; i < len(v); {
|
for i := 0; i < len(v); {
|
||||||
c, w := utf8.DecodeRuneInString(v[i:])
|
c, w := utf8.DecodeRuneInString(v[i:])
|
||||||
|
|
@ -149,16 +143,15 @@ func isValidVarName(v string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func isdigit(c rune) bool {
|
func isdigit(c rune) bool {
|
||||||
return '0' <= c && c <= '9'
|
return '0' <= c && c <= '9'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func isalpha(c rune) bool {
|
func isalpha(c rune) bool {
|
||||||
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
|
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
|
||||||
}
|
}
|
||||||
|
|
||||||
func isalnum(c rune) bool {
|
func isalnum(c rune) bool {
|
||||||
return isalpha(c) || isdigit(c)
|
return isalpha(c) || isdigit(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
type assignmentError struct {
|
type assignmentError struct {
|
||||||
|
|
@ -175,14 +168,12 @@ func (rs *ruleSet) executeAssignment(ts []token) *assignmentError {
|
||||||
ts[0]}
|
ts[0]}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// expanded variables
|
// expanded variables
|
||||||
vals := make([]string, 0)
|
vals := make([]string, 0)
|
||||||
for i := 1; i < len(ts); i++ {
|
for i := 1; i < len(ts); i++ {
|
||||||
vals = append(vals, expand(ts[i].val, rs.vars, true)...)
|
vals = append(vals, expand(ts[i].val, rs.vars, true)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
rs.vars[assignee] = vals
|
rs.vars[assignee] = vals
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue