Improve multiproduct_kati output

It now uses the same output style as ninja, overwriting status lines in
smart terminals.

Test: multiproduct_kati
Test: multiproduct_kati | cat
Change-Id: I8db5198ffdc5ebc5503241ac492379753d92978e
This commit is contained in:
Dan Willemsen
2017-05-06 16:58:26 -07:00
parent 02f3add3a3
commit a4e43a77d8
2 changed files with 87 additions and 17 deletions

View File

@@ -56,6 +56,79 @@ type Product struct {
config build.Config
}
type Status struct {
cur int
total int
failed int
ctx build.Context
haveBlankLine bool
smartTerminal bool
lock sync.Mutex
}
func NewStatus(ctx build.Context) *Status {
return &Status{
ctx: ctx,
haveBlankLine: true,
smartTerminal: ctx.IsTerminal(),
}
}
func (s *Status) SetTotal(total int) {
s.total = total
}
func (s *Status) Fail(product string, err error) {
s.Finish(product)
s.lock.Lock()
defer s.lock.Unlock()
if s.smartTerminal && !s.haveBlankLine {
fmt.Fprintln(s.ctx.Stdout())
s.haveBlankLine = true
}
s.failed++
fmt.Fprintln(s.ctx.Stderr(), "FAILED:", product)
s.ctx.Verboseln("FAILED:", product)
s.ctx.Println(err)
}
func (s *Status) Finish(product string) {
s.lock.Lock()
defer s.lock.Unlock()
s.cur++
line := fmt.Sprintf("[%d/%d] %s", s.cur, s.total, product)
if s.smartTerminal {
if max, ok := s.ctx.TermWidth(); ok {
if len(line) > max {
line = line[:max]
}
}
fmt.Fprint(s.ctx.Stdout(), "\r", line, "\x1b[K")
s.haveBlankLine = false
} else {
s.ctx.Println(line)
}
}
func (s *Status) Finished() int {
s.lock.Lock()
defer s.lock.Unlock()
if !s.haveBlankLine {
fmt.Fprintln(s.ctx.Stdout())
s.haveBlankLine = true
}
return s.failed
}
func main() {
log := logger.New(os.Stderr)
defer log.Cleanup()
@@ -80,7 +153,7 @@ func main() {
StdioInterface: build.StdioImpl{},
}}
failed := false
status := NewStatus(buildCtx)
config := build.NewConfig(buildCtx)
if *outDir == "" {
@@ -94,7 +167,7 @@ func main() {
if !*keep {
defer func() {
if !failed {
if status.Finished() == 0 {
os.RemoveAll(*outDir)
}
}()
@@ -114,8 +187,9 @@ func main() {
products := strings.Fields(vars["all_named_products"])
log.Verbose("Got product list:", products)
status.SetTotal(len(products))
var wg sync.WaitGroup
errs := make(chan error, len(products))
productConfigs := make(chan Product, len(products))
// Run the product config for every product in parallel
@@ -124,7 +198,7 @@ func main() {
go func(product string) {
defer wg.Done()
defer logger.Recover(func(err error) {
errs <- fmt.Errorf("Error building %s: %v", product, err)
status.Fail(product, err)
})
productOutDir := filepath.Join(config.OutDir(), product)
@@ -171,7 +245,7 @@ func main() {
for product := range productConfigs {
func() {
defer logger.Recover(func(err error) {
errs <- fmt.Errorf("Error building %s: %v", product.config.TargetProduct(), err)
status.Fail(product.config.TargetProduct(), err)
})
buildWhat := 0
@@ -185,22 +259,14 @@ func main() {
if !*keep {
os.RemoveAll(product.config.OutDir())
}
log.Println("Finished running for", product.config.TargetProduct())
status.Finish(product.config.TargetProduct())
}()
}
}()
}
go func() {
wg2.Wait()
close(errs)
}()
wg2.Wait()
for err := range errs {
failed = true
log.Print(err)
}
if failed {
log.Fatalln("Failed")
if count := status.Finished(); count > 0 {
log.Fatalln(count, "products failed")
}
}

View File

@@ -102,3 +102,7 @@ func (c ContextImpl) IsTerminal() bool {
}
return false
}
func (c ContextImpl) TermWidth() (int, bool) {
return termWidth(c.Stdout())
}