1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
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
pkg string
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`")
fs.StringVar(&f.pkg, "pkg", "", "package name (defaults to current directory name)")
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()
if f.pkg != "" {
pc.Pkg = f.pkg
}
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 }
|