mk/mk.go

254 lines
5.7 KiB
Go
Raw Normal View History

2013-02-25 21:25:25 -08:00
package main
import (
2013-03-03 17:51:00 -08:00
"flag"
"fmt"
"io/ioutil"
"os"
2013-03-03 18:57:14 -08:00
"sync"
2013-02-25 21:25:25 -08:00
)
2013-03-03 17:50:00 -08:00
// True if messages should be printed without fancy colors.
var nocolor bool = false
// True if we are no actualyl executing any recipes or updating any timestamps.
var dryrun bool = false
2013-03-04 00:06:24 -08:00
// True if we are ignoring timestamps and rebuilding everything.
var rebuildall bool = false
2013-03-03 18:57:14 -08:00
// Lock on standard out, messages don't get interleaved too much.
var mkMsgMutex sync.Mutex
// The maximum number of times an rule may be applied.
2013-03-04 00:06:24 -08:00
const maxRuleCnt = 3
// Limit the number of recipes executed simultaneously.
var subprocsAllowed int
var subprocsAllowedCond *sync.Cond = sync.NewCond(&sync.Mutex{})
// Wait until there is an available subprocess slot.
func reserveSubproc() {
subprocsAllowedCond.L.Lock()
for subprocsAllowed == 0 {
subprocsAllowedCond.Wait()
}
subprocsAllowed--
subprocsAllowedCond.L.Unlock()
}
// Free up another subprocess to run.
func finishSubproc() {
subprocsAllowedCond.L.Lock()
subprocsAllowed++
subprocsAllowedCond.Signal()
subprocsAllowedCond.L.Unlock()
}
2013-03-03 17:50:00 -08:00
// Ansi color codes.
const (
2013-03-03 17:51:00 -08:00
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"
2013-03-03 17:50:00 -08:00
)
func mk(rs *ruleSet, target string, dryrun bool) {
2013-03-03 17:51:00 -08:00
g := buildgraph(rs, target)
2013-03-04 00:06:24 -08:00
if g.root.exists && !rebuildall {
2013-03-03 18:57:14 -08:00
return
}
2013-03-03 17:51:00 -08:00
mkNode(g, g.root)
2013-03-03 17:50:00 -08:00
}
2013-03-03 17:50:00 -08:00
// Build a target in the graph.
//
// This selects an appropriate rule (edge) and builds all prerequisites
// concurrently.
//
func mkNode(g *graph, u *node) {
2013-03-03 17:51:00 -08:00
// 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
}
}()
// there's no fucking rules, dude
if len(u.prereqs) == 0 {
2013-03-03 23:32:36 -08:00
if !(u.r != nil && u.r.attributes.virtual) && !u.exists {
2013-03-03 17:51:00 -08:00
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)
}
}
// 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
// build prereqs that need building
e.r.mutex.Lock()
for i := range prereqs {
prereqs[i].mutex.Lock()
// needs to be built?
2013-03-04 00:06:24 -08:00
if !prereqs[i].exists || e.r.attributes.virtual || rebuildall || (u.exists && u.t.Before(prereqs[i].t)) {
2013-03-03 17:51:00 -08:00
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)
//}
s := <-prereqstat
pending--
if s == nodeStatusFailed {
finalstatus = nodeStatusFailed
}
}
// execute the recipe, unless the prereqs failed
2013-03-03 23:32:36 -08:00
if finalstatus != nodeStatusFailed && len(e.r.recipe) > 0 {
2013-03-04 00:06:24 -08:00
reserveSubproc()
2013-03-03 17:51:00 -08:00
if !dorecipe(u.name, u, e) {
finalstatus = nodeStatusFailed
}
2013-03-04 00:06:24 -08:00
finishSubproc()
2013-03-03 17:51:00 -08:00
}
2013-03-03 18:57:14 -08:00
//mkPrintSuccess("finished mking " + u.name)
2013-03-03 17:50:00 -08:00
}
func mkError(msg string) {
2013-03-03 17:51:00 -08:00
if !nocolor {
os.Stderr.WriteString(colorRed)
}
fmt.Fprintf(os.Stderr, "mk: %s\n", msg)
if !nocolor {
os.Stderr.WriteString(colorDefault)
}
os.Exit(1)
}
2013-03-03 17:50:00 -08:00
func mkPrintSuccess(msg string) {
2013-03-03 17:51:00 -08:00
if nocolor {
fmt.Println(msg)
} else {
fmt.Printf("%s%s%s\n", colorGreen, msg, colorDefault)
}
2013-03-03 17:50:00 -08:00
}
func mkPrintMessage(msg string) {
2013-03-03 18:57:14 -08:00
mkMsgMutex.Lock()
2013-03-03 17:51:00 -08:00
if nocolor {
fmt.Println(msg)
} else {
fmt.Printf("%s%s%s\n", colorBlue, msg, colorDefault)
}
2013-03-03 18:57:14 -08:00
mkMsgMutex.Unlock()
2013-03-03 17:50:00 -08:00
}
2013-03-03 18:57:14 -08:00
func mkPrintRecipe(target string, recipe string) {
mkMsgMutex.Lock()
if nocolor {
fmt.Printf("%s: ", target)
} else {
fmt.Printf("%s%s%s => %s", colorBlue, target, colorDefault, colorMagenta)
}
printIndented(os.Stdout, recipe, len(target)+4)
if len(recipe) == 0 {
os.Stdout.WriteString("\n")
2013-03-03 17:51:00 -08:00
}
if !nocolor {
os.Stdout.WriteString(colorDefault)
}
2013-03-03 18:57:14 -08:00
mkMsgMutex.Unlock()
2013-03-03 17:50:00 -08:00
}
2013-02-25 21:25:25 -08:00
func main() {
2013-03-03 17:51:00 -08:00
var mkfilepath string
flag.StringVar(&mkfilepath, "f", "mkfile", "use the given file as mkfile")
flag.BoolVar(&dryrun, "n", false, "print commands without actually executing")
2013-03-04 00:06:24 -08:00
flag.BoolVar(&rebuildall, "a", false, "force building of all dependencies")
flag.IntVar(&subprocsAllowed, "p", 64, "maximum number of jobs to execute in parallel")
2013-03-03 17:51:00 -08:00
flag.Parse()
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()
// 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
}
// 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)
}
2013-02-25 21:25:25 -08:00
}