blob: c4817637bf1a9e8332ff17f7357ed9b883417e32 [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package main implements the tast executable, used to build and run tests.
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"github.com/google/subcommands"
"golang.org/x/crypto/ssh/terminal"
"chromiumos/cmd/tast/logging"
)
const (
signalChannelSize = 3 // capacity of channel used to intercept signals
fancyVerboseLines = 30 // verbose log lines to display in "fancy" mode
)
var Version = "<unknown>" // Version info is filled in during emerge.
// newLogger creates a logging.Logger based on the supplied command-line flags.
func newLogger(fancy, verbose, logTime bool) (logging.Logger, error) {
if fancy {
l, err := logging.NewFancy(fancyVerboseLines)
if err != nil {
err = fmt.Errorf("-fancy unsupported: %v", err)
}
return l, err
}
var flags int
if logTime {
flags = log.LstdFlags
}
return logging.NewSimple(os.Stdout, flags, verbose), nil
}
// installSignalHandler starts a goroutine that attempts to do some minimal
// cleanup when the process is being terminated by a signal (which prevents
// deferred functions from running).
func installSignalHandler(lg logging.Logger) {
var st *terminal.State
fd := int(os.Stdin.Fd())
if terminal.IsTerminal(fd) {
var err error
if st, err = terminal.GetState(fd); err != nil {
lg.Log("Failed to get terminal state: ", err)
}
}
sc := make(chan os.Signal, signalChannelSize)
go func() {
for sig := range sc {
lg.Close()
if st != nil {
terminal.Restore(fd, st)
}
fmt.Fprintf(os.Stdout, "\nCaught %v signal; exiting\n", sig)
os.Exit(1)
}
}()
signal.Notify(sc, syscall.SIGINT, syscall.SIGKILL)
}
// doMain implements the main body of the program. It's a separate function so
// that its deferred functions will run before os.Exit makes the program exit
// immediately.
func doMain() int {
subcommands.Register(subcommands.HelpCommand(), "")
subcommands.Register(subcommands.FlagsCommand(), "")
subcommands.Register(subcommands.CommandsCommand(), "")
subcommands.Register(&buildCmd{}, "")
subcommands.Register(newListCmd(os.Stdout), "")
subcommands.Register(newRunCmd(), "")
subcommands.Register(&symbolizeCmd{}, "")
version := flag.Bool("version", false, "print version and exit")
fancy := flag.Bool("fancy", false, "use fancy logging")
verbose := flag.Bool("verbose", false, "use verbose logging")
logTime := flag.Bool("logtime", true, "include date/time headers in logs")
flag.Parse()
if *version {
fmt.Printf("tast version %s\n", Version)
return 0
}
lg, err := newLogger(*fancy, *verbose, *logTime)
if err != nil {
log.Fatal("Failed to initialize logging: ", err)
}
defer lg.Close()
ctx := logging.NewContext(context.Background(), lg)
installSignalHandler(lg)
return int(subcommands.Execute(ctx))
}
func main() {
os.Exit(doMain())
}