blob: 4180ca8e453b4a9e6d7699e8ebcdc33482e78fd6 [file] [log] [blame]
package main
import (
"errors"
"fmt"
"os"
"os/exec"
"time"
)
type Executor struct {
rules map[string]*Rule
}
func newExecutor() *Executor {
return &Executor{
rules: make(map[string]*Rule),
}
}
// TODO(ukai): use time.Time?
func getTimestamp(filename string) int64 {
st, err := os.Stat(filename)
if err != nil {
return -2
}
return st.ModTime().Unix()
}
func (ex *Executor) runCommands(cmds []string) error {
for _, cmd := range cmds {
fmt.Printf("%s\n", cmd)
args := []string{"/bin/sh", "-c", cmd}
cmd := exec.Cmd{
Path: args[0],
Args: args,
}
out, err := cmd.CombinedOutput()
if err != nil {
return err
}
success := false
if cmd.ProcessState != nil {
success = cmd.ProcessState.Success()
}
fmt.Printf("%s", out)
if !success {
return fmt.Errorf("command failed: %q", cmd)
}
}
return nil
}
func (ex *Executor) build(output string) (int64, error) {
Log("Building: %s", output)
outputTs := getTimestamp(output)
rule, present := ex.rules[output]
if !present {
if outputTs >= 0 {
return outputTs, nil
}
return outputTs, fmt.Errorf("no rule to make target %q", output)
}
latest := int64(-1)
for _, input := range rule.inputs {
ts, err := ex.build(input)
if err != nil {
return outputTs, err
}
if latest < ts {
latest = ts
}
}
if outputTs >= latest {
return outputTs, nil
}
err := ex.runCommands(rule.cmds)
if err != nil {
return outputTs, err
}
outputTs = getTimestamp(output)
if outputTs < 0 {
outputTs = time.Now().Unix()
}
return outputTs, nil
}
func (ex *Executor) exec(er *EvalResult, targets []string) error {
if len(er.rules) == 0 {
return errors.New("no targets.")
}
for _, rule := range er.rules {
if oldRule, present := ex.rules[rule.output]; present {
if len(oldRule.cmds) > 0 && len(rule.cmds) > 0 {
Warn(rule.filename, rule.cmdLineno, "overriding commands for target %q", rule.output)
Warn(oldRule.filename, oldRule.cmdLineno, "ignoring old commands for target %q", oldRule.output)
}
r := &Rule{}
*r = *rule
r.inputs = append(r.inputs, oldRule.inputs...)
ex.rules[rule.output] = r
} else {
ex.rules[rule.output] = rule
}
}
if len(targets) == 0 {
targets = append(targets, er.rules[0].output)
}
for _, target := range targets {
_, err := ex.build(target)
if err != nil {
return err
}
}
return nil
}
func Exec(er *EvalResult, targets []string) error {
ex := newExecutor()
return ex.exec(er, targets)
}