Skip to content

Commit aff260f

Browse files
authored
compileopts: improve error reporting of unsupported flags
1 parent a9ba6eb commit aff260f

File tree

4 files changed

+211
-9
lines changed

4 files changed

+211
-9
lines changed

compileopts/config.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ func (c *Config) NeedsStackObjects() bool {
129129
}
130130
}
131131

132-
// Scheduler returns the scheduler implementation. Valid values are "coroutines"
133-
// and "tasks".
132+
// Scheduler returns the scheduler implementation. Valid values are "none",
133+
//"coroutines" and "tasks".
134134
func (c *Config) Scheduler() string {
135135
if c.Options.Scheduler != "" {
136136
return c.Options.Scheduler

compileopts/options.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
package compileopts
22

3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
var (
9+
validGCOptions = []string{"none", "leaking", "extalloc", "conservative"}
10+
validSchedulerOptions = []string{"none", "tasks", "coroutines"}
11+
validPrintSizeOptions = []string{"none", "short", "full"}
12+
validPanicStrategyOptions = []string{"print", "trap"}
13+
)
14+
315
// Options contains extra options to give to the compiler. These options are
416
// usually passed from the command line.
517
type Options struct {
@@ -21,3 +33,53 @@ type Options struct {
2133
TestConfig TestConfig
2234
Programmer string
2335
}
36+
37+
// Verify performs a validation on the given options, raising an error if options are not valid.
38+
func (o *Options) Verify() error {
39+
if o.GC != "" {
40+
valid := isInArray(validGCOptions, o.GC)
41+
if !valid {
42+
return fmt.Errorf(`invalid gc option '%s': valid values are %s`,
43+
o.GC,
44+
strings.Join(validGCOptions, ", "))
45+
}
46+
}
47+
48+
if o.Scheduler != "" {
49+
valid := isInArray(validSchedulerOptions, o.Scheduler)
50+
if !valid {
51+
return fmt.Errorf(`invalid scheduler option '%s': valid values are %s`,
52+
o.Scheduler,
53+
strings.Join(validSchedulerOptions, ", "))
54+
}
55+
}
56+
57+
if o.PrintSizes != "" {
58+
valid := isInArray(validPrintSizeOptions, o.PrintSizes)
59+
if !valid {
60+
return fmt.Errorf(`invalid size option '%s': valid values are %s`,
61+
o.PrintSizes,
62+
strings.Join(validPrintSizeOptions, ", "))
63+
}
64+
}
65+
66+
if o.PanicStrategy != "" {
67+
valid := isInArray(validPanicStrategyOptions, o.PanicStrategy)
68+
if !valid {
69+
return fmt.Errorf(`invalid panic option '%s': valid values are %s`,
70+
o.PanicStrategy,
71+
strings.Join(validPanicStrategyOptions, ", "))
72+
}
73+
}
74+
75+
return nil
76+
}
77+
78+
func isInArray(arr []string, item string) bool {
79+
for _, i := range arr {
80+
if i == item {
81+
return true
82+
}
83+
}
84+
return false
85+
}

compileopts/options_test.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package compileopts_test
2+
3+
import (
4+
"errors"
5+
"testing"
6+
7+
"github.com/tinygo-org/tinygo/compileopts"
8+
)
9+
10+
func TestVerifyOptions(t *testing.T) {
11+
12+
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, extalloc, conservative`)
13+
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, coroutines`)
14+
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
15+
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
16+
17+
testCases := []struct {
18+
name string
19+
opts compileopts.Options
20+
expectedError error
21+
}{
22+
{
23+
name: "OptionsEmpty",
24+
opts: compileopts.Options{},
25+
},
26+
{
27+
name: "InvalidGCOption",
28+
opts: compileopts.Options{
29+
GC: "incorrect",
30+
},
31+
expectedError: expectedGCError,
32+
},
33+
{
34+
name: "GCOptionNone",
35+
opts: compileopts.Options{
36+
GC: "none",
37+
},
38+
},
39+
{
40+
name: "GCOptionLeaking",
41+
opts: compileopts.Options{
42+
GC: "leaking",
43+
},
44+
},
45+
{
46+
name: "GCOptionExtalloc",
47+
opts: compileopts.Options{
48+
GC: "extalloc",
49+
},
50+
},
51+
{
52+
name: "GCOptionConservative",
53+
opts: compileopts.Options{
54+
GC: "conservative",
55+
},
56+
},
57+
{
58+
name: "InvalidSchedulerOption",
59+
opts: compileopts.Options{
60+
Scheduler: "incorrect",
61+
},
62+
expectedError: expectedSchedulerError,
63+
},
64+
{
65+
name: "SchedulerOptionNone",
66+
opts: compileopts.Options{
67+
Scheduler: "none",
68+
},
69+
},
70+
{
71+
name: "SchedulerOptionTasks",
72+
opts: compileopts.Options{
73+
Scheduler: "tasks",
74+
},
75+
},
76+
{
77+
name: "SchedulerOptionCoroutines",
78+
opts: compileopts.Options{
79+
Scheduler: "coroutines",
80+
},
81+
},
82+
{
83+
name: "InvalidPrintSizeOption",
84+
opts: compileopts.Options{
85+
PrintSizes: "incorrect",
86+
},
87+
expectedError: expectedPrintSizeError,
88+
},
89+
{
90+
name: "PrintSizeOptionNone",
91+
opts: compileopts.Options{
92+
PrintSizes: "none",
93+
},
94+
},
95+
{
96+
name: "PrintSizeOptionShort",
97+
opts: compileopts.Options{
98+
PrintSizes: "short",
99+
},
100+
},
101+
{
102+
name: "PrintSizeOptionFull",
103+
opts: compileopts.Options{
104+
PrintSizes: "full",
105+
},
106+
},
107+
{
108+
name: "InvalidPanicOption",
109+
opts: compileopts.Options{
110+
PanicStrategy: "incorrect",
111+
},
112+
expectedError: expectedPanicStrategyError,
113+
},
114+
{
115+
name: "PanicOptionPrint",
116+
opts: compileopts.Options{
117+
PanicStrategy: "print",
118+
},
119+
},
120+
{
121+
name: "PanicOptionTrap",
122+
opts: compileopts.Options{
123+
PanicStrategy: "trap",
124+
},
125+
},
126+
}
127+
128+
for _, tc := range testCases {
129+
t.Run(tc.name, func(t *testing.T) {
130+
err := tc.opts.Verify()
131+
if tc.expectedError != err {
132+
if tc.expectedError.Error() != err.Error() {
133+
t.Errorf("expected %v, got %v", tc.expectedError, err)
134+
}
135+
}
136+
})
137+
}
138+
}

main.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ func main() {
710710
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
711711
gc := flag.String("gc", "", "garbage collector to use (none, leaking, extalloc, conservative)")
712712
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
713-
scheduler := flag.String("scheduler", "", "which scheduler to use (coroutines, tasks)")
713+
scheduler := flag.String("scheduler", "", "which scheduler to use (none, coroutines, tasks)")
714714
printIR := flag.Bool("printir", false, "print LLVM IR")
715715
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
716716
verifyIR := flag.Bool("verifyir", false, "run extra verification steps on LLVM IR")
@@ -770,12 +770,6 @@ func main() {
770770
options.LDFlags = strings.Split(*ldFlags, " ")
771771
}
772772

773-
if *panicStrategy != "print" && *panicStrategy != "trap" {
774-
fmt.Fprintln(os.Stderr, "Panic strategy must be either print or trap.")
775-
usage()
776-
os.Exit(1)
777-
}
778-
779773
var err error
780774
if options.HeapSize, err = parseSize(*heapSize); err != nil {
781775
fmt.Fprintln(os.Stderr, "Could not read heap size:", *heapSize)
@@ -785,6 +779,13 @@ func main() {
785779

786780
os.Setenv("CC", "clang -target="+*target)
787781

782+
err = options.Verify()
783+
if err != nil {
784+
fmt.Fprintln(os.Stderr, err.Error())
785+
usage()
786+
os.Exit(1)
787+
}
788+
788789
switch command {
789790
case "build":
790791
if *outpath == "" {
@@ -803,6 +804,7 @@ func main() {
803804
if options.Target == "" && filepath.Ext(*outpath) == ".wasm" {
804805
options.Target = "wasm"
805806
}
807+
806808
err := Build(pkgName, *outpath, options)
807809
handleCompilerError(err)
808810
case "build-library":

0 commit comments

Comments
 (0)