Fix an issue with expansion of escaped newlines. Use the currently set mkfile vars as environment vars for the shell command invoked by "expandBackQuoted".
210 lines
3.9 KiB
Go
210 lines
3.9 KiB
Go
// Various function for dealing with recipes.
|
|
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// 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, mincol int) string {
|
|
// trim leading whitespace
|
|
reader := bufio.NewReader(strings.NewReader(s))
|
|
output := ""
|
|
for {
|
|
line, err := reader.ReadString('\n')
|
|
col := 0
|
|
i := 0
|
|
for i < len(line) && col < mincol {
|
|
c, w := utf8.DecodeRuneInString(line[i:])
|
|
if strings.IndexRune(" \t\n", c) >= 0 {
|
|
col += 1
|
|
i += w
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
output += line[i:]
|
|
|
|
if err != nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
return output
|
|
}
|
|
|
|
// Indent each line of a recipe.
|
|
func printIndented(out io.Writer, s string, ind int) {
|
|
indentation := strings.Repeat(" ", ind)
|
|
reader := bufio.NewReader(strings.NewReader(s))
|
|
firstline := true
|
|
for {
|
|
line, err := reader.ReadString('\n')
|
|
if len(line) > 0 {
|
|
if !firstline {
|
|
io.WriteString(out, indentation)
|
|
}
|
|
io.WriteString(out, line)
|
|
}
|
|
if err != nil {
|
|
break
|
|
}
|
|
firstline = false
|
|
}
|
|
}
|
|
|
|
// Execute a recipe.
|
|
func dorecipe(target string, u *node, e *edge, dryrun bool) 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}
|
|
}
|
|
}
|
|
|
|
// 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["prereq"] = prereqs
|
|
|
|
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:]
|
|
}
|
|
|
|
mkPrintRecipe(target, input, e.r.attributes.quiet)
|
|
|
|
if dryrun {
|
|
return true
|
|
}
|
|
|
|
_, success := subprocess(
|
|
sh,
|
|
args,
|
|
nil,
|
|
input,
|
|
false)
|
|
|
|
return success
|
|
}
|
|
|
|
// Execute a subprocess (typically a recipe).
|
|
//
|
|
// Args:
|
|
// program: Program path or name located in PATH
|
|
// input: String piped into the program's stdin
|
|
// capture_out: If true, capture and return the program's stdout rather than echoing it.
|
|
//
|
|
// Returns
|
|
// (output, success)
|
|
// output is an empty string of catputer_out is false, or the collected output from the profram is true.
|
|
//
|
|
// success is true if the exit code was 0 and false otherwise
|
|
//
|
|
func subprocess(program string,
|
|
args []string,
|
|
env []string,
|
|
input string,
|
|
capture_out bool) (string, bool) {
|
|
program_path, err := exec.LookPath(program)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
proc_args := []string{program}
|
|
proc_args = append(proc_args, args...)
|
|
|
|
stdin_pipe_read, stdin_pipe_write, err := os.Pipe()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
attr := os.ProcAttr{Env: env, Files: []*os.File{stdin_pipe_read, os.Stdout, os.Stderr}}
|
|
|
|
output := make([]byte, 0)
|
|
capture_done := make(chan bool)
|
|
if capture_out {
|
|
stdout_pipe_read, stdout_pipe_write, err := os.Pipe()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
attr.Files[1] = stdout_pipe_write
|
|
|
|
go func() {
|
|
buf := make([]byte, 1024)
|
|
for {
|
|
n, err := stdout_pipe_read.Read(buf)
|
|
|
|
if err == io.EOF && n == 0 {
|
|
break
|
|
} else if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
output = append(output, buf[:n]...)
|
|
}
|
|
|
|
capture_done <- true
|
|
}()
|
|
}
|
|
|
|
proc, err := os.StartProcess(program_path, proc_args, &attr)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
go func() {
|
|
_, err := stdin_pipe_write.WriteString(input)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
err = stdin_pipe_write.Close()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
state, err := proc.Wait()
|
|
|
|
if attr.Files[1] != os.Stdout {
|
|
attr.Files[1].Close()
|
|
}
|
|
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// wait until stdout copying in finished
|
|
if capture_out {
|
|
<-capture_done
|
|
}
|
|
|
|
return string(output), state.Success()
|
|
}
|