File: cli.go

package info (click to toggle)
golang-github-mmcloughlin-avo 0.5.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 15,024 kB
  • sloc: xml: 71,029; asm: 14,862; sh: 194; makefile: 21; ansic: 11
file content (171 lines) | stat: -rw-r--r-- 3,533 bytes parent folder | download | duplicates (2)
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 }