package phases import ( "fmt" "os" "path/filepath" "time" "deb-bootc-compose/internal/types" ) // BuildPhase handles package building using the build system type BuildPhase struct { PhaseBase } // NewBuildPhase creates a new BuildPhase func NewBuildPhase() *BuildPhase { return &BuildPhase{ PhaseBase: NewPhaseBase("build", []string{"gather"}), } } // Run executes the build phase func (p *BuildPhase) Run(engine types.Engine) error { logger := engine.GetLogger() logger.Printf("Starting build phase") // Get build system buildSystem := engine.GetBuildSystem() if buildSystem == nil { logger.Printf("No build system configured, skipping build phase") return nil } // Test build system connection if err := buildSystem.TestConnection(); err != nil { return fmt.Errorf("build system connection failed: %w", err) } // Get treefile and process variants treefile := engine.GetTreefile() for _, variant := range treefile.Variants { for _, arch := range variant.Architectures { logger.Printf("Building variant %s for architecture %s", variant.Name, arch) // Create build work directory buildWorkDir := filepath.Join(engine.GetWorkDir(), "variants", variant.Name, arch, "build") if err := os.MkdirAll(buildWorkDir, 0755); err != nil { return fmt.Errorf("failed to create build work directory: %w", err) } // Build packages for this variant/architecture if err := p.buildVariant(engine, buildWorkDir, variant, arch); err != nil { return fmt.Errorf("failed to build variant %s for architecture %s: %w", variant.Name, arch, err) } logger.Printf("Completed building variant %s for architecture %s", variant.Name, arch) } } logger.Printf("Build phase completed successfully") return nil } // buildVariant builds packages for a specific variant and architecture func (p *BuildPhase) buildVariant(engine types.Engine, workDir string, variant types.Variant, arch string) error { logger := engine.GetLogger() buildSystem := engine.GetBuildSystem() // Get packages for this variant packages := engine.GetTreefile().GetPackagesForVariant(variant.Name, arch) // Create build environment logger.Printf("Creating build environment for architecture %s", arch) buildEnv, err := buildSystem.CreateBuildEnvironment(arch) if err != nil { return fmt.Errorf("failed to create build environment: %w", err) } // Ensure cleanup of build environment defer func() { if err := buildSystem.CleanupBuildEnvironment(buildEnv); err != nil { logger.Printf("Warning: failed to cleanup build environment: %v", err) } }() // Collect all packages to build allPackages := make([]string, 0) allPackages = append(allPackages, packages.Required...) allPackages = append(allPackages, packages.Optional...) allPackages = append(allPackages, packages.Recommended...) allPackages = append(allPackages, packages.Development...) allPackages = append(allPackages, packages.Kernel...) allPackages = append(allPackages, packages.Bootloader...) // Remove duplicates uniquePackages := make(map[string]bool) for _, pkg := range allPackages { uniquePackages[pkg] = true } // Build each package buildResults := make([]*types.BuildResult, 0) for pkgName := range uniquePackages { logger.Printf("Building package: %s", pkgName) // Build the package result, err := buildSystem.BuildPackage(pkgName, arch) if err != nil { logger.Printf("Warning: failed to build package %s: %v", pkgName, err) continue } // Wait for build to complete completedResult, err := buildSystem.WaitForBuild(result.ID, 30*time.Minute) if err != nil { logger.Printf("Warning: failed to wait for build of package %s: %v", pkgName, err) continue } if completedResult == nil { logger.Printf("Warning: build result is nil for package %s", pkgName) continue } buildResults = append(buildResults, completedResult) logger.Printf("Successfully built package: %s", pkgName) } // Write build results if err := p.writeBuildResults(workDir, buildResults); err != nil { return fmt.Errorf("failed to write build results: %w", err) } // Write build summary if err := p.writeBuildSummary(workDir, buildResults, variant, arch); err != nil { return fmt.Errorf("failed to write build summary: %w", err) } logger.Printf("Built %d packages for variant %s on architecture %s", len(buildResults), variant.Name, arch) return nil } // writeBuildResults writes build results to files func (p *BuildPhase) writeBuildResults(workDir string, results []*types.BuildResult) error { // Create results directory resultsDir := filepath.Join(workDir, "results") if err := os.MkdirAll(resultsDir, 0755); err != nil { return fmt.Errorf("failed to create results directory: %w", err) } // Write successful builds successful := make([]string, 0) failed := make([]string, 0) for _, result := range results { if result.Result == "success" { successful = append(successful, result.PackageName) } else { failed = append(failed, result.PackageName) } } // Write successful packages if err := p.writePackageList(resultsDir, "successful", successful); err != nil { return fmt.Errorf("failed to write successful packages: %w", err) } // Write failed packages if err := p.writePackageList(resultsDir, "failed", failed); err != nil { return fmt.Errorf("failed to write failed packages: %w", err) } return nil } // writeBuildSummary writes a summary of the build process func (p *BuildPhase) writeBuildSummary(workDir string, results []*types.BuildResult, variant types.Variant, arch string) error { summaryFile := filepath.Join(workDir, "build-summary.txt") summary := fmt.Sprintf(`Build Summary for Variant: %s Architecture: %s Timestamp: %s Total Packages: %d Successful Builds: %d Failed Builds: %d Successful Packages: `, variant.Name, arch, time.Now().Format(time.RFC3339), len(results), len(results), 0) // For now, assume all successful // Count successful builds successfulCount := 0 for _, result := range results { if result.Result == "success" { successfulCount++ summary += fmt.Sprintf(" - %s\n", result.PackageName) } } summary += fmt.Sprintf("\nFailed Packages:\n") failedCount := 0 for _, result := range results { if result.Result != "success" { failedCount++ summary += fmt.Sprintf(" - %s (%s)\n", result.PackageName, result.Result) } } // Update counts summary = fmt.Sprintf(`Build Summary for Variant: %s Architecture: %s Timestamp: %s Total Packages: %d Successful Builds: %d Failed Builds: %d Successful Packages: `, variant.Name, arch, time.Now().Format(time.RFC3339), len(results), successfulCount, failedCount) + summary if err := os.WriteFile(summaryFile, []byte(summary), 0644); err != nil { return fmt.Errorf("failed to write build summary: %w", err) } return nil } // writePackageList writes a list of packages to a file func (p *BuildPhase) writePackageList(dir, name string, packages []string) error { if len(packages) == 0 { return nil } filename := filepath.Join(dir, name+".txt") content := "" for _, pkg := range packages { content += pkg + "\n" } if err := os.WriteFile(filename, []byte(content), 0644); err != nil { return fmt.Errorf("failed to write package list %s: %w", name, err) } return nil }