Files
judge/runner/compiler.go
Mikhail Kornilovich 128a64a609
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
add new build system
2026-04-11 01:51:38 +03:00

199 lines
4.3 KiB
Go

package runner
import (
"fmt"
"runtime"
"sort"
"strings"
"github.com/Mond1c/judge/dsl"
)
type CompilerClass int
const (
CompilerUnknown CompilerClass = iota
CompilerGNU
CompilerMSVC
)
type Toolchain struct {
Class CompilerClass
Binary string
Name string
}
func ResolveToolchain(ccEnv string) Toolchain {
if ccEnv == "" {
ccEnv = defaultCC()
}
lower := strings.ToLower(ccEnv)
switch lower {
case "gcc", "g++":
return Toolchain{Class: CompilerGNU, Binary: ccEnv, Name: "gcc"}
case "clang", "clang++":
return Toolchain{Class: CompilerGNU, Binary: ccEnv, Name: "clang"}
case "cl", "cl.exe", "msvc":
return Toolchain{Class: CompilerMSVC, Binary: "cl", Name: "msvc"}
case "clang-cl", "clang-cl.exe":
return Toolchain{Class: CompilerMSVC, Binary: "clang-cl", Name: "clang-cl"}
case "cc":
return Toolchain{Class: CompilerGNU, Binary: "cc", Name: "cc"}
default:
return Toolchain{Class: CompilerGNU, Binary: ccEnv, Name: lower}
}
}
func ResolveToolchainSpec(spec *dsl.ToolchainSpec) Toolchain {
inferred := ResolveToolchain(spec.Name)
binary := spec.Binary
if binary == "" {
binary = inferred.Binary
}
var class CompilerClass
switch spec.Class {
case "gnu":
class = CompilerGNU
case "msvc":
class = CompilerMSVC
default:
class = inferred.Class
}
return Toolchain{Class: class, Binary: binary, Name: spec.Name}
}
func defaultCC() string {
if runtime.GOOS == "windows" {
return "cl"
}
return "cc"
}
func Compile(cfg dsl.BuildConfig, tc Toolchain, outputPath string) ([]string, error) {
switch tc.Class {
case CompilerGNU:
return compileGNU(cfg, tc, outputPath), nil
case CompilerMSVC:
return compileMSVC(cfg, tc, outputPath), nil
default:
return nil, fmt.Errorf("unknown compiler class for toolchain %q", tc.Name)
}
}
func compileGNU(cfg dsl.BuildConfig, tc Toolchain, outputPath string) []string {
argv := []string{tc.Binary}
if cfg.Standard != "" {
argv = append(argv, "-std="+cfg.Standard)
}
switch cfg.Profile {
case dsl.ProfileRelease:
argv = append(argv, "-O2")
case dsl.ProfileDebug:
argv = append(argv, "-O0", "-g")
case dsl.ProfileSanitized:
argv = append(argv, "-O1", "-g", "-fno-omit-frame-pointer")
}
switch cfg.Warnings {
case dsl.WarningsStrict:
argv = append(argv, "-Wall", "-Wextra")
case dsl.WarningsPedantic:
argv = append(argv, "-Wall", "-Wextra", "-Wpedantic")
}
if len(cfg.Sanitize) > 0 {
argv = append(argv, "-fsanitize="+strings.Join(cfg.Sanitize, ","))
}
for _, inc := range cfg.Includes {
argv = append(argv, "-I"+inc)
}
for _, k := range sortedKeys(cfg.Defines) {
v := cfg.Defines[k]
if v == "" {
argv = append(argv, "-D"+k)
} else {
argv = append(argv, fmt.Sprintf("-D%s=%s", k, v))
}
}
argv = append(argv, cfg.Extra...)
argv = append(argv, cfg.Sources...)
argv = append(argv, "-o", outputPath)
for _, lib := range cfg.Link {
argv = append(argv, "-l"+lib)
}
return argv
}
func compileMSVC(cfg dsl.BuildConfig, tc Toolchain, outputPath string) []string {
argv := []string{tc.Binary, "/nologo"}
if cfg.Standard != "" {
argv = append(argv, "/std:"+cfg.Standard)
}
switch cfg.Profile {
case dsl.ProfileRelease:
argv = append(argv, "/O2")
case dsl.ProfileDebug:
argv = append(argv, "/Od", "/Zi")
case dsl.ProfileSanitized:
argv = append(argv, "/Od", "/Zi", "/fsanitize=address")
}
switch cfg.Warnings {
case dsl.WarningsStrict:
argv = append(argv, "/W4")
case dsl.WarningsPedantic:
argv = append(argv, "/W4", "/permissive-")
}
if containsString(cfg.Sanitize, "address") && cfg.Profile != dsl.ProfileSanitized {
argv = append(argv, "/fsanitize=address")
}
for _, inc := range cfg.Includes {
argv = append(argv, "/I"+inc)
}
for _, k := range sortedKeys(cfg.Defines) {
v := cfg.Defines[k]
if v == "" {
argv = append(argv, "/D"+k)
} else {
argv = append(argv, fmt.Sprintf("/D%s=%s", k, v))
}
}
argv = append(argv, cfg.Extra...)
argv = append(argv, cfg.Sources...)
argv = append(argv, "/Fe:"+outputPath)
return argv
}
func sortedKeys(m map[string]string) []string {
if len(m) == 0 {
return nil
}
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}
func containsString(xs []string, x string) bool {
for _, v := range xs {
if v == x {
return true
}
}
return false
}