package dsl // BuildProfile is a named compilation profile. The translator maps it into // compiler-specific flag sets at execution time (e.g. ProfileRelease → "-O2" // on gnu-like compilers, "/O2" on msvc). type BuildProfile int const ( ProfileUnset BuildProfile = iota ProfileRelease ProfileDebug ProfileSanitized ) func (p BuildProfile) String() string { switch p { case ProfileRelease: return "release" case ProfileDebug: return "debug" case ProfileSanitized: return "sanitized" default: return "unset" } } // WarningLevel describes how strict the compiler should be about warnings. type WarningLevel int const ( WarningsUnset WarningLevel = iota WarningsDefault WarningsStrict WarningsPedantic ) func (w WarningLevel) String() string { switch w { case WarningsDefault: return "default" case WarningsStrict: return "strict" case WarningsPedantic: return "pedantic" default: return "unset" } } // BuildConfig describes one structured build variant. It is the new-style // replacement for the free-form `build "shell-string"` field. // // A top-level `build_defaults { ... }` in the suite file produces a // BuildConfig stored on File.BuildDefaults. Each `build "name" { ... }` // block produces an entry in File.Builds; the effective configuration used // by the runner is BuildDefaults merged with the named block, then merged // with the OS-specific override (Linux / Windows / Darwin) when present. // // Zero-valued fields inherit from the parent during merge. Slice and map // fields accumulate rather than replace. type BuildConfig struct { // Name of the variant. Empty on BuildDefaults and on OS-override sub-blocks. Name string Language string // e.g. "c", "c++" Standard string // e.g. "c11", "c++17" Sources []string // globs, relative to work dir Includes []string // include search paths Output string // binary name (OS-specific extension added automatically) Profile BuildProfile Warnings WarningLevel Sanitize []string Wrapper string // e.g. "address", "undefined", "thread" Defines map[string]string Link []string // libraries to link against (e.g. "pthread", "m") Extra []string // raw passthrough flags // Filters — empty means "applies to any". A build is skipped at runtime // if the current OS or compiler is not in the list. Platforms []string // "linux", "windows", "darwin" Compilers []string // "gcc", "clang", "msvc" // OS-specific overrides. Only one level of nesting is allowed: these // sub-configs must not themselves contain Linux/Windows/Darwin blocks. Linux *BuildConfig Windows *BuildConfig Darwin *BuildConfig } // MergeFrom layers src on top of dst in place. Non-zero scalar fields in src // overwrite dst; slices and maps accumulate. The Name and OS override fields // on src are intentionally ignored — merging never copies the hierarchy, // only the leaves. func (dst *BuildConfig) MergeFrom(src *BuildConfig) { if src == nil { return } if src.Language != "" { dst.Language = src.Language } if src.Standard != "" { dst.Standard = src.Standard } if src.Output != "" { dst.Output = src.Output } if src.Profile != ProfileUnset { dst.Profile = src.Profile } if src.Warnings != WarningsUnset { dst.Warnings = src.Warnings } if src.Wrapper != "" { dst.Wrapper = src.Wrapper } dst.Sources = append(dst.Sources, src.Sources...) dst.Includes = append(dst.Includes, src.Includes...) dst.Sanitize = append(dst.Sanitize, src.Sanitize...) dst.Link = append(dst.Link, src.Link...) dst.Extra = append(dst.Extra, src.Extra...) dst.Platforms = append(dst.Platforms, src.Platforms...) dst.Compilers = append(dst.Compilers, src.Compilers...) if len(src.Defines) > 0 { if dst.Defines == nil { dst.Defines = map[string]string{} } for k, v := range src.Defines { dst.Defines[k] = v } } } // Resolve returns the effective BuildConfig for the given OS by merging // BuildDefaults → this block → the matching OS override. The result is a // fresh value; the receiver is not mutated. func (b *BuildConfig) Resolve(defaults *BuildConfig, os string) BuildConfig { var out BuildConfig out.MergeFrom(defaults) out.MergeFrom(b) out.Name = b.Name var osOverride *BuildConfig switch os { case "linux": osOverride = b.Linux case "windows": osOverride = b.Windows case "darwin": osOverride = b.Darwin } out.MergeFrom(osOverride) return out } // AppliesTo reports whether this build should run on (os, compiler). // An empty Platforms/Compilers list means no filter on that axis. func (b *BuildConfig) AppliesTo(os, compiler string) bool { if len(b.Platforms) > 0 && !contains(b.Platforms, os) { return false } if len(b.Compilers) > 0 && !contains(b.Compilers, compiler) { return false } return true } func contains(xs []string, x string) bool { for _, v := range xs { if v == x { return true } } return false } type ToolchainSpec struct { Name string Platforms []string Binary string Class string }