Files
judge/dsl/build_parser.go
Mikhail Kornilovich 7ec3a43c7a
All checks were successful
build-dsl-smoke / Build judge (push) Successful in 12s
build-dsl-smoke / debug / clang / linux (push) Successful in 6s
build-dsl-smoke / debug / gcc / linux (push) Successful in 8s
build-dsl-smoke / release / clang / linux (push) Successful in 8s
build-dsl-smoke / release / gcc / linux (push) Successful in 6s
build-dsl-smoke / sanitized / clang / linux (push) Successful in 8s
build-dsl-smoke / sanitized / gcc / linux (push) Successful in 7s
build-dsl-smoke / debug / clang / windows (push) Successful in 13s
build-dsl-smoke / debug-valgrind / gcc / linux (push) Successful in 14s
build-dsl-smoke / release / clang / windows (push) Successful in 16s
build-dsl-smoke / debug / msvc / windows (push) Successful in 18s
build-dsl-smoke / release / msvc / windows (push) Successful in 17s
build-dsl-smoke / SUMMARY (push) Successful in 4s
Release / Build & publish (push) Successful in 48s
1. New build system
Reviewed-on: #1
2026-04-12 07:59:38 +00:00

356 lines
7.7 KiB
Go

package dsl
import "fmt"
func (p *Parser) parseBuildBlock(name string) (*BuildConfig, error) {
return p.parseBuildBlockInner(name, false)
}
func (p *Parser) parseBuildBlockInner(name string, inOSOverride bool) (*BuildConfig, error) {
if _, err := p.expect(TOKEN_LBRACE); err != nil {
return nil, err
}
bc := &BuildConfig{Name: name}
for !p.isRBrace() {
t := p.peek()
if t.Type != TOKEN_IDENT {
return nil, fmt.Errorf("%d:%d: unexpected token %q in build block", t.Line, t.Col, t.Value)
}
switch t.Value {
case "language":
p.advance()
s, err := p.parseAssignString()
if err != nil {
return nil, err
}
bc.Language = s
case "standard":
p.advance()
s, err := p.parseAssignString()
if err != nil {
return nil, err
}
bc.Standard = s
case "output":
p.advance()
s, err := p.parseAssignString()
if err != nil {
return nil, err
}
bc.Output = s
case "wrapper":
p.advance()
s, err := p.parseAssignString()
if err != nil {
return nil, err
}
bc.Wrapper = s
case "sources":
p.advance()
xs, err := p.parseAssignStringList()
if err != nil {
return nil, err
}
bc.Sources = xs
case "includes":
p.advance()
xs, err := p.parseAssignStringList()
if err != nil {
return nil, err
}
bc.Includes = xs
case "sanitize":
p.advance()
xs, err := p.parseAssignStringList()
if err != nil {
return nil, err
}
bc.Sanitize = xs
case "link":
p.advance()
xs, err := p.parseAssignStringList()
if err != nil {
return nil, err
}
bc.Link = xs
case "extra":
p.advance()
xs, err := p.parseAssignStringList()
if err != nil {
return nil, err
}
bc.Extra = xs
case "platforms":
p.advance()
xs, err := p.parseAssignStringList()
if err != nil {
return nil, err
}
if err := validatePlatformList(xs, t.Line, t.Col); err != nil {
return nil, err
}
bc.Platforms = xs
case "compilers":
p.advance()
xs, err := p.parseAssignStringList()
if err != nil {
return nil, err
}
bc.Compilers = xs
case "profile":
p.advance()
if _, err := p.expect(TOKEN_ASSIGN); err != nil {
return nil, err
}
id, err := p.expect(TOKEN_IDENT)
if err != nil {
return nil, err
}
prof, err := parseProfileIdent(id.Value, id.Line, id.Col)
if err != nil {
return nil, err
}
bc.Profile = prof
case "warnings":
p.advance()
if _, err := p.expect(TOKEN_ASSIGN); err != nil {
return nil, err
}
id, err := p.expect(TOKEN_IDENT)
if err != nil {
return nil, err
}
w, err := parseWarningsIdent(id.Value, id.Line, id.Col)
if err != nil {
return nil, err
}
bc.Warnings = w
case "define":
p.advance()
if _, err := p.expect(TOKEN_LPAREN); err != nil {
return nil, err
}
key, err := p.expect(TOKEN_STRING)
if err != nil {
return nil, err
}
if _, err := p.expect(TOKEN_RPAREN); err != nil {
return nil, err
}
if _, err := p.expect(TOKEN_ASSIGN); err != nil {
return nil, err
}
val, err := p.expect(TOKEN_STRING)
if err != nil {
return nil, err
}
if bc.Defines == nil {
bc.Defines = map[string]string{}
}
bc.Defines[key.Value] = val.Value
case "linux", "windows", "darwin":
if inOSOverride {
return nil, fmt.Errorf("%d:%d: OS override %q cannot be nested inside another OS override", t.Line, t.Col, t.Value)
}
osName := t.Value
p.advance()
sub, err := p.parseBuildBlockInner("", true)
if err != nil {
return nil, err
}
switch osName {
case "linux":
if bc.Linux != nil {
return nil, fmt.Errorf("%d:%d: duplicate linux override", t.Line, t.Col)
}
bc.Linux = sub
case "windows":
if bc.Windows != nil {
return nil, fmt.Errorf("%d:%d: duplicate windows override", t.Line, t.Col)
}
bc.Windows = sub
case "darwin":
if bc.Darwin != nil {
return nil, fmt.Errorf("%d:%d: duplicate darwin override", t.Line, t.Col)
}
bc.Darwin = sub
}
default:
return nil, fmt.Errorf("%d:%d: unknown field %q in build block", t.Line, t.Col, t.Value)
}
}
if _, err := p.expect(TOKEN_RBRACE); err != nil {
return nil, err
}
return bc, nil
}
func (p *Parser) parseAssignString() (string, error) {
if _, err := p.expect(TOKEN_ASSIGN); err != nil {
return "", err
}
s, err := p.expect(TOKEN_STRING)
if err != nil {
return "", err
}
return s.Value, nil
}
func (p *Parser) parseAssignStringList() ([]string, error) {
if _, err := p.expect(TOKEN_ASSIGN); err != nil {
return nil, err
}
return p.parseStringList()
}
func parseProfileIdent(v string, line, col int) (BuildProfile, error) {
switch v {
case "release":
return ProfileRelease, nil
case "debug":
return ProfileDebug, nil
case "sanitized":
return ProfileSanitized, nil
default:
return ProfileUnset, fmt.Errorf("%d:%d: unknown profile %q (expected release/debug/sanitized)", line, col, v)
}
}
func parseWarningsIdent(v string, line, col int) (WarningLevel, error) {
switch v {
case "default":
return WarningsDefault, nil
case "strict":
return WarningsStrict, nil
case "pedantic":
return WarningsPedantic, nil
default:
return WarningsUnset, fmt.Errorf("%d:%d: unknown warnings level %q (expected default/strict/pedantic)", line, col, v)
}
}
func validatePlatformList(xs []string, line, col int) error {
for _, x := range xs {
switch x {
case "linux", "windows", "darwin":
default:
return fmt.Errorf("%d:%d: unknown platform %q (expected linux/windows/darwin)", line, col, x)
}
}
return nil
}
func (p *Parser) parseToolchainsBlock() ([]*ToolchainSpec, error) {
if _, err := p.expect(TOKEN_LBRACE); err != nil {
return nil, err
}
var specs []*ToolchainSpec
seen := map[string]bool{}
for !p.isRBrace() {
nameTok := p.peek()
if nameTok.Type != TOKEN_IDENT && nameTok.Type != TOKEN_STRING {
return nil, fmt.Errorf("%d:%d: expected toolchain name, got %q", nameTok.Line, nameTok.Col, nameTok.Value)
}
p.advance()
name := nameTok.Value
if seen[name] {
return nil, fmt.Errorf("%d:%d: duplicate toolchain %q", nameTok.Line, nameTok.Col, name)
}
seen[name] = true
spec, err := p.parseToolchainEntry(name)
if err != nil {
return nil, err
}
specs = append(specs, spec)
}
if _, err := p.expect(TOKEN_RBRACE); err != nil {
return nil, err
}
return specs, nil
}
func (p *Parser) parseToolchainEntry(name string) (*ToolchainSpec, error) {
if _, err := p.expect(TOKEN_LBRACE); err != nil {
return nil, err
}
spec := &ToolchainSpec{Name: name}
for !p.isRBrace() {
t := p.peek()
if t.Type != TOKEN_IDENT {
return nil, fmt.Errorf("%d:%d: unexpected token %q in toolchain block", t.Line, t.Col, t.Value)
}
switch t.Value {
case "platforms":
p.advance()
xs, err := p.parseAssignStringList()
if err != nil {
return nil, err
}
if err := validatePlatformList(xs, t.Line, t.Col); err != nil {
return nil, err
}
spec.Platforms = xs
case "binary":
p.advance()
s, err := p.parseAssignString()
if err != nil {
return nil, err
}
spec.Binary = s
case "class":
p.advance()
if _, err := p.expect(TOKEN_ASSIGN); err != nil {
return nil, err
}
id, err := p.expect(TOKEN_IDENT)
if err != nil {
return nil, err
}
switch id.Value {
case "gnu", "msvc":
default:
return nil, fmt.Errorf("%d:%d: unknown compiler class %q (expected gnu/msvc)", id.Line, id.Col, id.Value)
}
spec.Class = id.Value
default:
return nil, fmt.Errorf("%d:%d: unknown field %q in toolchain block", t.Line, t.Col, t.Value)
}
}
if _, err := p.expect(TOKEN_RBRACE); err != nil {
return nil, err
}
if len(spec.Platforms) == 0 {
return nil, fmt.Errorf("toolchain %q: platforms is required", name)
}
return spec, nil
}