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:
@@ -56,6 +56,79 @@ type Product struct {
|
|||||||
config build.Config
|
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() {
|
func main() {
|
||||||
log := logger.New(os.Stderr)
|
log := logger.New(os.Stderr)
|
||||||
defer log.Cleanup()
|
defer log.Cleanup()
|
||||||
@@ -80,7 +153,7 @@ func main() {
|
|||||||
StdioInterface: build.StdioImpl{},
|
StdioInterface: build.StdioImpl{},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
failed := false
|
status := NewStatus(buildCtx)
|
||||||
|
|
||||||
config := build.NewConfig(buildCtx)
|
config := build.NewConfig(buildCtx)
|
||||||
if *outDir == "" {
|
if *outDir == "" {
|
||||||
@@ -94,7 +167,7 @@ func main() {
|
|||||||
|
|
||||||
if !*keep {
|
if !*keep {
|
||||||
defer func() {
|
defer func() {
|
||||||
if !failed {
|
if status.Finished() == 0 {
|
||||||
os.RemoveAll(*outDir)
|
os.RemoveAll(*outDir)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -114,8 +187,9 @@ func main() {
|
|||||||
products := strings.Fields(vars["all_named_products"])
|
products := strings.Fields(vars["all_named_products"])
|
||||||
log.Verbose("Got product list:", products)
|
log.Verbose("Got product list:", products)
|
||||||
|
|
||||||
|
status.SetTotal(len(products))
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
errs := make(chan error, len(products))
|
|
||||||
productConfigs := make(chan Product, len(products))
|
productConfigs := make(chan Product, len(products))
|
||||||
|
|
||||||
// Run the product config for every product in parallel
|
// Run the product config for every product in parallel
|
||||||
@@ -124,7 +198,7 @@ func main() {
|
|||||||
go func(product string) {
|
go func(product string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
defer logger.Recover(func(err error) {
|
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)
|
productOutDir := filepath.Join(config.OutDir(), product)
|
||||||
@@ -171,7 +245,7 @@ func main() {
|
|||||||
for product := range productConfigs {
|
for product := range productConfigs {
|
||||||
func() {
|
func() {
|
||||||
defer logger.Recover(func(err error) {
|
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
|
buildWhat := 0
|
||||||
@@ -185,22 +259,14 @@ func main() {
|
|||||||
if !*keep {
|
if !*keep {
|
||||||
os.RemoveAll(product.config.OutDir())
|
os.RemoveAll(product.config.OutDir())
|
||||||
}
|
}
|
||||||
log.Println("Finished running for", product.config.TargetProduct())
|
status.Finish(product.config.TargetProduct())
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
go func() {
|
|
||||||
wg2.Wait()
|
wg2.Wait()
|
||||||
close(errs)
|
|
||||||
}()
|
|
||||||
|
|
||||||
for err := range errs {
|
if count := status.Finished(); count > 0 {
|
||||||
failed = true
|
log.Fatalln(count, "products failed")
|
||||||
log.Print(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if failed {
|
|
||||||
log.Fatalln("Failed")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -102,3 +102,7 @@ func (c ContextImpl) IsTerminal() bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c ContextImpl) TermWidth() (int, bool) {
|
||||||
|
return termWidth(c.Stdout())
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user