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