All checks were successful
build-dsl-smoke / Discover matrix (push) Successful in 8s
build-dsl-smoke / Build judge (push) Successful in 11s
build-dsl-smoke / ${{ matrix.cell.build }} / ${{ matrix.cell.toolchain }} / ${{ matrix.cell.platform }} (push) Successful in 5s
memory-limit / Build judge (pull_request) Successful in 10s
build-dsl-smoke / SUMMARY (push) Successful in 3s
memory-limit / Linux / gcc (pull_request) Successful in 9s
memory-limit / Linux / clang (pull_request) Successful in 13s
memory-limit / Windows / clang (pull_request) Successful in 16s
memory-limit / Windows / msvc (pull_request) Successful in 17s
356 lines
7.7 KiB
Go
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
|
|
}
|