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 }