package main import ( "fmt" "os" "strings" "github.com/Mond1c/judge/dsl" "github.com/Mond1c/judge/reporter" "github.com/Mond1c/judge/runner" ) const usage = `judge — CI/CD testing system for student solutions Usage: judge [flags] judge aggregate Flags: --json output as JSON instead of text --wrapper exec wrapper (e.g. "valgrind --error-exitcode=99") --binary name of executable produced by build (overrides .jdg) --help show help Example: judge lab1.jdg ./student-solution judge lab1.jdg ./student-solution --json judge aggregate reports/ ` func main() { args := os.Args[1:] if len(args) == 0 || hasFlag(args, "--help") || hasFlag(args, "-h") { fmt.Print(usage) os.Exit(0) } if len(args) >= 1 && args[0] == "aggregate" { runAggregate(args[1:]) return } jsonOutput := hasFlag(args, "--json") wrapper := flagValue(args, "--wrapper") binary := flagValue(args, "--binary") positional := positionalArgs(args) if len(positional) < 2 { fmt.Fprintf(os.Stderr, "error: need and \n\n%s", usage) os.Exit(1) } testFile := positional[0] solutionDir := positional[1] src, err := os.ReadFile(testFile) if err != nil { fatalf("cannot read %q: %v", testFile, err) } f, warns, err := dsl.Parse(string(src)) if err != nil { fatalf("parse error in %q:\n %v", testFile, err) } for _, w := range warns { fmt.Fprintf(os.Stderr, "warning: %s\n", w) } if _, err := os.Stat(solutionDir); err != nil { fatalf("solution dir %q not found: %v", solutionDir, err) } r := runner.New(f, runner.Config{ WorkDir: solutionDir, BinaryName: binary, Wrapper: wrapper, }) result := r.Run() if jsonOutput { if err := reporter.JSON(os.Stdout, result); err != nil { fatalf("json output error: %v", err) } } else { reporter.Text(os.Stdout, result) } if result.TotalScore < 0.9999 { os.Exit(1) } } func runAggregate(args []string) { if len(args) < 1 { fatalf("usage: judge aggregate ") } if err := reporter.Aggregate(os.Stdout, args[0]); err != nil { fatalf("%v", err) } } func fatalf(msg string, args ...any) { fmt.Fprintf(os.Stderr, "error: "+msg+"\n", args...) os.Exit(1) } func hasFlag(args []string, name string) bool { for _, a := range args { if a == name { return true } } return false } func flagValue(args []string, name string) string { prefix := name + "=" for i, a := range args { if a == name && i+1 < len(args) { return args[i+1] } if strings.HasPrefix(a, prefix) { return a[len(prefix):] } } return "" } func positionalArgs(args []string) []string { known := map[string]bool{"--json": true, "--help": true, "-h": true} withValue := map[string]bool{"--wrapper": true, "--binary": true} var out []string skip := false for _, a := range args { if skip { skip = false continue } if known[a] { continue } if withValue[a] { skip = true continue } if strings.HasPrefix(a, "--wrapper=") || strings.HasPrefix(a, "--binary=") { continue } out = append(out, a) } return out }