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 }