bldr/util/gocompiler/tinygo-options.go
Rama master · réplica de lectura del repositorio original.
package gocompiler
import (
"os"
"slices"
"strings"
"time"
"github.com/pkg/errors"
)
const (
// TinyGoProfileEnv selects the TinyGo build profile.
TinyGoProfileEnv = "BLDR_TINYGO_PROFILE"
// TinyGoOptEnv overrides the TinyGo optimization level.
TinyGoOptEnv = "BLDR_TINYGO_OPT"
// TinyGoPanicStrategyEnv controls the TinyGo panic strategy.
TinyGoPanicStrategyEnv = "BLDR_TINYGO_PANIC"
// TinyGoGCEnv overrides the TinyGo garbage collector.
TinyGoGCEnv = "BLDR_TINYGO_GC"
// TinyGoSchedulerEnv overrides the TinyGo scheduler.
TinyGoSchedulerEnv = "BLDR_TINYGO_SCHEDULER"
// TinyGoStackSizeEnv overrides the TinyGo goroutine stack size.
TinyGoStackSizeEnv = "BLDR_TINYGO_STACK_SIZE"
// TinyGoLLVMFeaturesEnv overrides the TinyGo LLVM feature list.
TinyGoLLVMFeaturesEnv = "BLDR_TINYGO_LLVM_FEATURES"
// TinyGoInterpTimeoutEnv overrides TinyGo interp optimization timeout.
TinyGoInterpTimeoutEnv = "BLDR_TINYGO_INTERP_TIMEOUT"
// TinyGoDebugInfoEnv retains TinyGo debug information in browser wasm.
TinyGoDebugInfoEnv = "BLDR_TINYGO_DEBUG"
// TinyGoTraceEnv prints TinyGo's internal build commands.
TinyGoTraceEnv = "BLDR_TINYGO_TRACE"
// TinyGoWorkEnv keeps TinyGo's temporary work directory for diagnostics.
TinyGoWorkEnv = "BLDR_TINYGO_WORK"
// TinyGoProfileDefault uses the optimized TinyGo defaults.
TinyGoProfileDefault = "default"
// TinyGoProfileFast uses a local development TinyGo profile.
TinyGoProfileFast = "fast"
// TinyGoProfileOptimized uses the optimized TinyGo profile.
TinyGoProfileOptimized = "optimized"
// TinyGoDefaultStackSize is the minimum stack headroom for TinyGo wasm
// plugins that run browser crypto, storage, and SRPC workloads.
TinyGoDefaultStackSize = "256KB"
)
var tinyGoStartupCacheEnvKeys = []string{
TinyGoProfileEnv,
TinyGoOptEnv,
TinyGoPanicStrategyEnv,
TinyGoGCEnv,
TinyGoSchedulerEnv,
TinyGoStackSizeEnv,
TinyGoLLVMFeaturesEnv,
TinyGoInterpTimeoutEnv,
TinyGoDebugInfoEnv,
TinyGoTraceEnv,
TinyGoWorkEnv,
}
// TinyGoOptions configures TinyGo compiler arguments.
type TinyGoOptions struct {
// Profile selects the default argument set.
Profile string
// Opt overrides the optimization level.
Opt string
// PanicStrategy overrides the panic strategy.
PanicStrategy string
// GC overrides the garbage collector.
GC string
// Scheduler overrides the scheduler.
Scheduler string
// StackSize overrides the goroutine stack size.
StackSize string
// LLVMFeatures overrides the comma-separated LLVM feature list.
LLVMFeatures string
// InterpTimeout overrides the TinyGo interp optimization timeout.
InterpTimeout string
// Trace prints TinyGo's internal build commands.
Trace string
// Work keeps TinyGo's temporary work directory.
Work string
}
// TinyGoStartupCacheEnvKeys returns env keys that affect TinyGo artifact identity.
func TinyGoStartupCacheEnvKeys() []string {
return slices.Clone(tinyGoStartupCacheEnvKeys)
}
// TinyGoOptionsFromEnv resolves TinyGo options from the process environment.
func TinyGoOptionsFromEnv() TinyGoOptions {
return TinyGoOptions{
Profile: os.Getenv(TinyGoProfileEnv),
Opt: os.Getenv(TinyGoOptEnv),
PanicStrategy: os.Getenv(TinyGoPanicStrategyEnv),
GC: os.Getenv(TinyGoGCEnv),
Scheduler: os.Getenv(TinyGoSchedulerEnv),
StackSize: os.Getenv(TinyGoStackSizeEnv),
LLVMFeatures: os.Getenv(TinyGoLLVMFeaturesEnv),
InterpTimeout: os.Getenv(TinyGoInterpTimeoutEnv),
Trace: os.Getenv(TinyGoTraceEnv),
Work: os.Getenv(TinyGoWorkEnv),
}
}
// GetDefaultTinygoArgs returns the TinyGo args selected by environment.
func GetDefaultTinygoArgs() ([]string, error) {
return TinyGoArgs(TinyGoOptionsFromEnv())
}
// TinyGoArgs converts TinyGoOptions into compiler args.
func TinyGoArgs(opts TinyGoOptions) ([]string, error) {
// Resolve the TinyGo profile that supplies compiler defaults.
profile, err := resolveTinyGoProfile(opts.Profile)
if err != nil {
return nil, err
}
// Resolve the TinyGo optimization level for the selected profile.
opt, err := resolveTinyGoOpt(profile, opts.Opt)
if err != nil {
return nil, err
}
// Resolve the TinyGo panic strategy and LLVM feature set.
panicStrategy, err := resolveTinyGoPanicStrategy(opts.PanicStrategy)
if err != nil {
return nil, err
}
features, err := resolveTinyGoLLVMFeatures(opts.LLVMFeatures)
if err != nil {
return nil, err
}
// Assemble the TinyGo panic, LLVM and optimization arguments.
args := []string{
"-panic=" + panicStrategy,
"-llvm-features=" + strings.Join(features, ","),
}
if opt != "" {
args = append([]string{"-opt=" + opt}, args...)
}
// Resolve the TinyGo garbage collector argument.
gc, err := resolveTinyGoGC(profile, opts.GC)
if err != nil {
return nil, err
}
if gc != "" {
args = append(args, "-gc="+gc)
}
// Validate and append the TinyGo scheduler override.
if scheduler := strings.TrimSpace(opts.Scheduler); scheduler != "" {
if err := validateTinyGoArgValue(TinyGoSchedulerEnv, scheduler); err != nil {
return nil, err
}
args = append(args, "-scheduler="+scheduler)
}
// Resolve the TinyGo goroutine stack argument.
stackSize, err := resolveTinyGoStackSize(opts.StackSize)
if err != nil {
return nil, err
}
if stackSize != "" {
args = append(args, "-stack-size="+stackSize)
}
// Resolve the TinyGo interpreter timeout argument.
interpTimeout, err := resolveTinyGoInterpTimeout(profile, opts.InterpTimeout)
if err != nil {
return nil, err
}
if interpTimeout != "" {
args = append(args, "-interp-timeout="+interpTimeout)
}
// Resolve whether TinyGo should print build commands.
trace, err := tinyGoBoolEnabled(TinyGoTraceEnv, opts.Trace)
if err != nil {
return nil, err
}
if trace {
args = append(args, "-x")
}
// Resolve whether TinyGo should retain its build directory.
work, err := tinyGoBoolEnabled(TinyGoWorkEnv, opts.Work)
if err != nil {
return nil, err
}
if work {
args = append(args, "-work")
}
return args, nil
}
// ResolveTinyGoPanicStrategy resolves the configured TinyGo panic strategy.
func ResolveTinyGoPanicStrategy() (string, error) {
return resolveTinyGoPanicStrategy(os.Getenv(TinyGoPanicStrategyEnv))
}
// TinyGoDebugInfoEnabled reports whether TinyGo should retain debug info.
func TinyGoDebugInfoEnabled() (bool, error) {
return tinyGoBoolEnabled(TinyGoDebugInfoEnv, os.Getenv(TinyGoDebugInfoEnv))
}
func tinyGoBoolEnabled(envKey, raw string) (bool, error) {
switch strings.TrimSpace(raw) {
case "", "0", "false", "no", "off":
return false, nil
case "1", "true", "yes", "on":
return true, nil
default:
return false, errors.Errorf("unsupported %s value %q, expected true or false", envKey, raw)
}
}
func resolveTinyGoProfile(profile string) (string, error) {
switch strings.TrimSpace(profile) {
case "", TinyGoProfileDefault, TinyGoProfileOptimized:
return TinyGoProfileOptimized, nil
case TinyGoProfileFast:
return TinyGoProfileFast, nil
default:
return "", errors.Errorf("unsupported %s value %q, expected default, fast, or optimized", TinyGoProfileEnv, profile)
}
}
func resolveTinyGoOpt(profile, rawOpt string) (string, error) {
// Resolve and validate the TinyGo optimization level.
opt := strings.TrimSpace(rawOpt)
if opt == "" && profile == TinyGoProfileFast {
opt = "1"
}
if opt == "" && profile == TinyGoProfileOptimized {
opt = "2"
}
if opt == "" {
return "", nil
}
if err := validateTinyGoOpt(opt); err != nil {
return "", err
}
return opt, nil
}
func resolveTinyGoPanicStrategy(panicStrategy string) (string, error) {
switch strings.TrimSpace(panicStrategy) {
case "", "print":
return "print", nil
case "trap":
return "trap", nil
default:
return "", errors.Errorf("unsupported %s value %q, expected trap or print", TinyGoPanicStrategyEnv, panicStrategy)
}
}
func resolveTinyGoStackSize(rawStackSize string) (string, error) {
// Resolve and validate the TinyGo goroutine stack size.
stackSize := strings.TrimSpace(rawStackSize)
if stackSize == "" {
return TinyGoDefaultStackSize, nil
}
if stackSize == "default" {
return "", nil
}
if err := validateTinyGoArgValue(TinyGoStackSizeEnv, stackSize); err != nil {
return "", err
}
return stackSize, nil
}
func validateTinyGoOpt(opt string) error {
switch opt {
case "0", "1", "2", "s", "z":
return nil
default:
return errors.Errorf("unsupported %s value %q, expected 0, 1, 2, s, or z", TinyGoOptEnv, opt)
}
}
func resolveTinyGoGC(profile, rawGC string) (string, error) {
// Resolve and validate the TinyGo garbage collector.
gc := strings.TrimSpace(rawGC)
if gc == "" && profile == TinyGoProfileFast {
gc = "leaking"
}
if gc == "" {
return "", nil
}
if err := validateTinyGoArgValue(TinyGoGCEnv, gc); err != nil {
return "", err
}
return gc, nil
}
func resolveTinyGoLLVMFeatures(raw string) ([]string, error) {
// Resolve and validate the TinyGo LLVM feature list.
raw = strings.TrimSpace(raw)
if raw == "" {
return GetDefaultTinygoLlvmFeatures(), nil
}
features := strings.Split(raw, ",")
for _, feature := range features {
if feature == "" {
return nil, errors.Errorf("unsupported %s value %q: empty feature", TinyGoLLVMFeaturesEnv, raw)
}
if err := validateTinyGoArgValue(TinyGoLLVMFeaturesEnv, feature); err != nil {
return nil, err
}
}
return features, nil
}
func validateTinyGoArgValue(envKey, value string) error {
if strings.ContainsAny(value, " \t\r\n,") {
return errors.Errorf("unsupported %s value %q: values cannot contain whitespace or commas", envKey, value)
}
return nil
}
func resolveTinyGoInterpTimeout(profile, rawTimeout string) (string, error) {
// Resolve and validate the TinyGo interpreter timeout.
timeout := strings.TrimSpace(rawTimeout)
if timeout == "" && profile == TinyGoProfileFast {
timeout = "10m"
}
if timeout == "" {
return "", nil
}
if _, err := time.ParseDuration(timeout); err != nil {
return "", errors.Wrapf(err, "unsupported %s value %q", TinyGoInterpTimeoutEnv, timeout)
}
return timeout, nil
}