package builder import ( "context" "fmt" "log" "os" "os/exec" "path/filepath" "strings" ) // compileGoProjectPlatform runs the Go (or garble) compiler for a specific target. // ctx cancellation kills the compiler process immediately — used by the forge cancel API. func (h *Handler) compileGoProjectPlatform(ctx context.Context, dir, outputPath, ldflags string, tags []string, obfuscate bool, platform BuildPlatform) ([]byte, error) { env := append(os.Environ(), "GOOS="+platform.GOOS, "GOARCH="+platform.GOARCH, "CGO_ENABLED=0", ) buildArgs := []string{"build", "-trimpath", "-ldflags", ldflags, "-o", outputPath} if len(tags) > 0 { buildArgs = append(buildArgs, "-tags", strings.Join(tags, ",")) } buildArgs = append(buildArgs, ".") // Garble works with any target OS from any host OS; the old windows-only guard was wrong. useGarble := obfuscate && h.garblePath != "" if obfuscate && !useGarble { log.Printf("[Forge] obfuscation requested but garble not in PATH — building plain binary") } var cmd *exec.Cmd if useGarble { garbleArgs := append([]string{"-literals", "-tiny"}, buildArgs...) cmd = exec.CommandContext(ctx, h.garblePath, garbleArgs...) } else { cmd = exec.CommandContext(ctx, h.goBinPath, buildArgs...) } cmd.Dir = dir cmd.Env = env out, err := cmd.CombinedOutput() if err != nil { if ctx.Err() != nil { return out, fmt.Errorf("build cancelled") } return out, fmt.Errorf("compile failed (%s): %s", platform.Label(), strings.TrimSpace(string(out))) } return out, nil } // compileWorker compiles one agent binary for a single OS/arch target. func (h *Handler) compileWorker(ctx context.Context, agentDir, buildDir string, req *BuildRequest, buildID string, platform BuildPlatform, fusionWorker bool) (string, error) { name := workerFileName(req.WorkerName, platform, fusionWorker) outputPath := filepath.Join(buildDir, platform.Label(), name) if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil { return "", err } configDir := filepath.Join(agentDir, "config") if err := os.MkdirAll(configDir, 0755); err != nil { return "", err } if err := os.WriteFile(filepath.Join(configDir, "builtin.go"), []byte(h.generateBuiltinConfig(buildID, req)), 0644); err != nil { return "", fmt.Errorf("write builtin config: %w", err) } ldflags := ldflagsFor(req, platform) extra, err := injectPolymorph(agentDir, buildID) if err != nil { log.Printf("[Forge] polymorph inject: %v", err) } else { ldflags += extra } obfuscated := h.shouldObfuscate(req) && h.garblePath != "" if _, err := h.compileGoProjectPlatform(ctx, agentDir, outputPath, ldflags, h.buildTagsFor(req), obfuscated, platform); err != nil { return "", err } return outputPath, nil }