166 lines
3.3 KiB
Go
166 lines
3.3 KiB
Go
|
package build
|
||
|
|
||
|
import (
|
||
|
"flag"
|
||
|
"io"
|
||
|
"log"
|
||
|
"os"
|
||
|
"runtime/pprof"
|
||
|
|
||
|
"github.com/mmcloughlin/avo/pass"
|
||
|
"github.com/mmcloughlin/avo/printer"
|
||
|
)
|
||
|
|
||
|
// Config contains options for an avo main function.
|
||
|
type Config struct {
|
||
|
ErrOut io.Writer
|
||
|
MaxErrors int // max errors to report; 0 means unlimited
|
||
|
CPUProfile io.WriteCloser
|
||
|
Passes []pass.Interface
|
||
|
}
|
||
|
|
||
|
// Main is the standard main function for an avo program. This extracts the
|
||
|
// result from the build Context (logging and exiting on error), and performs
|
||
|
// configured passes.
|
||
|
func Main(cfg *Config, context *Context) int {
|
||
|
diag := log.New(cfg.ErrOut, "", 0)
|
||
|
|
||
|
if cfg.CPUProfile != nil {
|
||
|
defer cfg.CPUProfile.Close()
|
||
|
if err := pprof.StartCPUProfile(cfg.CPUProfile); err != nil {
|
||
|
diag.Println("could not start CPU profile: ", err)
|
||
|
return 1
|
||
|
}
|
||
|
defer pprof.StopCPUProfile()
|
||
|
}
|
||
|
|
||
|
f, err := context.Result()
|
||
|
if err != nil {
|
||
|
LogError(diag, err, cfg.MaxErrors)
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
p := pass.Concat(cfg.Passes...)
|
||
|
if err := p.Execute(f); err != nil {
|
||
|
diag.Println(err)
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
// Flags represents CLI flags for an avo program.
|
||
|
type Flags struct {
|
||
|
errout *outputValue
|
||
|
allerrors bool
|
||
|
cpuprof *outputValue
|
||
|
printers []*printerValue
|
||
|
}
|
||
|
|
||
|
// NewFlags initializes avo flags for the given FlagSet.
|
||
|
func NewFlags(fs *flag.FlagSet) *Flags {
|
||
|
f := &Flags{}
|
||
|
|
||
|
f.errout = newOutputValue(os.Stderr)
|
||
|
fs.Var(f.errout, "log", "diagnostics output")
|
||
|
|
||
|
fs.BoolVar(&f.allerrors, "e", false, "no limit on number of errors reported")
|
||
|
|
||
|
f.cpuprof = newOutputValue(nil)
|
||
|
fs.Var(f.cpuprof, "cpuprofile", "write cpu profile to `file`")
|
||
|
|
||
|
goasm := newPrinterValue(printer.NewGoAsm, os.Stdout)
|
||
|
fs.Var(goasm, "out", "assembly output")
|
||
|
f.printers = append(f.printers, goasm)
|
||
|
|
||
|
stubs := newPrinterValue(printer.NewStubs, nil)
|
||
|
fs.Var(stubs, "stubs", "go stub file")
|
||
|
f.printers = append(f.printers, stubs)
|
||
|
|
||
|
return f
|
||
|
}
|
||
|
|
||
|
// Config builds a configuration object based on flag values.
|
||
|
func (f *Flags) Config() *Config {
|
||
|
pc := printer.NewGoRunConfig()
|
||
|
passes := []pass.Interface{pass.Compile}
|
||
|
for _, pv := range f.printers {
|
||
|
p := pv.Build(pc)
|
||
|
if p != nil {
|
||
|
passes = append(passes, p)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cfg := &Config{
|
||
|
ErrOut: f.errout.w,
|
||
|
MaxErrors: 10,
|
||
|
CPUProfile: f.cpuprof.w,
|
||
|
Passes: passes,
|
||
|
}
|
||
|
|
||
|
if f.allerrors {
|
||
|
cfg.MaxErrors = 0
|
||
|
}
|
||
|
|
||
|
return cfg
|
||
|
}
|
||
|
|
||
|
type outputValue struct {
|
||
|
w io.WriteCloser
|
||
|
filename string
|
||
|
}
|
||
|
|
||
|
func newOutputValue(dflt io.WriteCloser) *outputValue {
|
||
|
return &outputValue{w: dflt}
|
||
|
}
|
||
|
|
||
|
func (o *outputValue) String() string {
|
||
|
if o == nil {
|
||
|
return ""
|
||
|
}
|
||
|
return o.filename
|
||
|
}
|
||
|
|
||
|
func (o *outputValue) Set(s string) error {
|
||
|
o.filename = s
|
||
|
if s == "-" {
|
||
|
o.w = nopwritecloser{os.Stdout}
|
||
|
return nil
|
||
|
}
|
||
|
f, err := os.Create(s)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
o.w = f
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type printerValue struct {
|
||
|
*outputValue
|
||
|
Builder printer.Builder
|
||
|
}
|
||
|
|
||
|
func newPrinterValue(b printer.Builder, dflt io.WriteCloser) *printerValue {
|
||
|
return &printerValue{
|
||
|
outputValue: newOutputValue(dflt),
|
||
|
Builder: b,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (p *printerValue) Build(cfg printer.Config) pass.Interface {
|
||
|
if p.outputValue.w == nil {
|
||
|
return nil
|
||
|
}
|
||
|
return &pass.Output{
|
||
|
Writer: p.outputValue.w,
|
||
|
Printer: p.Builder(cfg),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// nopwritecloser wraps a Writer and provides a null implementation of Close().
|
||
|
type nopwritecloser struct {
|
||
|
io.Writer
|
||
|
}
|
||
|
|
||
|
func (nopwritecloser) Close() error { return nil }
|