From 467d1d3d446f4bf456c880cdbfa1e053728bccaf Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Sun, 3 Mar 2013 18:57:14 -0800 Subject: [PATCH] Prettier printing. --- expand.go | 8 ++++---- lex.go | 8 ++++++-- mk.go | 30 ++++++++++++++++++++++-------- parse.go | 4 ++-- recipe.go | 49 +++++++++++++++++++++++++++++++++++++++---------- 5 files changed, 73 insertions(+), 26 deletions(-) diff --git a/expand.go b/expand.go index 6e3d12f..2232d58 100644 --- a/expand.go +++ b/expand.go @@ -196,14 +196,14 @@ func expandSigils(input string, vars map[string][]string) []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 { + off := strings.IndexAny(input[i:], "$\\") + if off < 0 { expanded += input[i:] break } + expanded += input[i : i+off] + i += off - expanded += input[i:j] - i = j c, w := utf8.DecodeRuneInString(input[i:]) if c == '$' { i += w diff --git a/lex.go b/lex.go index 7da84b0..1bc9e80 100644 --- a/lex.go +++ b/lex.go @@ -49,6 +49,7 @@ type token struct { typ tokenType // token type val string // token string line int // line where it was found + col int // column on which the token began } func (t *token) String() string { @@ -65,6 +66,7 @@ type lexer struct { input string // input string to be lexed output chan token // channel on which tokens are sent start int // token beginning + startcol int // column on which the token begins pos int // position within input line int // line within input col int // column within input @@ -129,11 +131,13 @@ func (l *lexer) next() rune { func (l *lexer) skip() { l.next() l.start = l.pos + l.startcol = l.col } func (l *lexer) emit(typ tokenType) { - l.output <- token{typ, l.input[l.start:l.pos], l.line} + l.output <- token{typ, l.input[l.start:l.pos], l.line, l.startcol} l.start = l.pos + l.startcol = 0 } // Consume the next run if it is in the given string. @@ -189,7 +193,7 @@ func (l *lexer) skipUntil(invalid string) { // Start a new lexer to lex the given input. func lex(input string) (*lexer, chan token) { - l := &lexer{input: input, output: make(chan token), line: 1, indented: true} + l := &lexer{input: input, output: make(chan token), line: 1, col: 0, indented: true} go l.run() return l, l.output } diff --git a/mk.go b/mk.go index 8e00284..ae60661 100644 --- a/mk.go +++ b/mk.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "os" + "sync" ) // True if messages should be printed without fancy colors. @@ -13,6 +14,9 @@ var nocolor bool = false // True if we are no actualyl executing any recipes or updating any timestamps. var dryrun bool = false +// 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. const max_rule_cnt = 3 @@ -29,7 +33,9 @@ const ( func mk(rs *ruleSet, target string, dryrun bool) { g := buildgraph(rs, target) - //g.visualize(os.Stdout) + if g.root.exists { + return + } mkNode(g, g.root) } @@ -129,13 +135,13 @@ func mkNode(g *graph, u *node) { // execute the recipe, unless the prereqs failed if finalstatus != nodeStatusFailed { - mkPrintMessage("mking " + u.name) + //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) { @@ -158,26 +164,34 @@ func mkPrintSuccess(msg string) { } func mkPrintMessage(msg string) { + mkMsgMutex.Lock() if nocolor { fmt.Println(msg) } else { fmt.Printf("%s%s%s\n", colorBlue, msg, colorDefault) } + mkMsgMutex.Unlock() } -func mkPrintRecipe(msg string) { - if !nocolor { - os.Stdout.WriteString(colorYellow) +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") } - printIndented(os.Stdout, msg) if !nocolor { os.Stdout.WriteString(colorDefault) } + mkMsgMutex.Unlock() } 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() diff --git a/parse.go b/parse.go index e3d82b6..3da732f 100644 --- a/parse.go +++ b/parse.go @@ -76,7 +76,7 @@ func parseInto(input string, name string, rules *ruleSet) { // insert a dummy newline to allow parsing of any assignments or recipeless // rules to finish. - state = state(p, token{tokenNewline, "\n", l.line}) + state = state(p, token{tokenNewline, "\n", l.line, l.col}) // TODO: Error when state != parseTopLevel } @@ -339,7 +339,7 @@ func parseRecipe(p *parser, t token) parserStateFun { } if t.typ == tokenRecipe { - r.recipe = t.val + r.recipe = stripIndentation(t.val, t.col) } p.rules.add(r) diff --git a/recipe.go b/recipe.go index 0a3f636..f5cec9a 100644 --- a/recipe.go +++ b/recipe.go @@ -10,28 +10,57 @@ import ( "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) string { - -//} - -// Indent each line of a recipe. -func printIndented(out io.Writer, s string) { +func stripIndentation(s string, mincol int) string { + // trim leading whitespace reader := bufio.NewReader(strings.NewReader(s)) + output := "" for { line, err := reader.ReadString('\n') - if len(line) > 0 { - io.WriteString(out, " ") - io.WriteString(out, line) + 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. @@ -70,7 +99,7 @@ func dorecipe(target string, u *node, e *edge) bool { } if !e.r.attributes.quiet { - mkPrintRecipe(input) + mkPrintRecipe(target, input) } if dryrun {