@@ -21,8 +21,10 @@ import (
2121 "os/exec"
2222 "path/filepath"
2323 "regexp"
24+ "runtime"
2425 "sort"
2526 "strings"
27+ "sync"
2628
2729 "pault.ag/go/debian/control"
2830 "pault.ag/go/debian/version"
@@ -100,6 +102,14 @@ var (
100102 false ,
101103 "Output results in JSON format (currently only works in combination with -dry_run)" )
102104
105+ parallel = flag .Bool ("parallel" ,
106+ false ,
107+ "Build packages in parallel" )
108+
109+ jobs = flag .Int ("jobs" ,
110+ runtime .NumCPU (),
111+ "Number of parallel build jobs (default: number of CPU cores)" )
112+
103113 listsPrefixRe = regexp .MustCompile (`/([^/]*_dists_.*)_InRelease$` )
104114)
105115
@@ -429,13 +439,105 @@ func getAptIndexPaths(dist string) ([]string, []string) {
429439 return fallbackIndexPaths ()
430440}
431441
442+ type buildJob struct {
443+ src string
444+ version version.Version
445+ }
446+
447+ func buildPackagesSequential (builder * sbuild , rebuild map [string ][]version.Version ) (map [string ]* buildResult , []dryRunBuild ) {
448+ buildresults := make (map [string ]* buildResult )
449+ var dryRunBuilds []dryRunBuild
450+ cnt := 1
451+
452+ for src , versions := range rebuild {
453+ sort .Sort (sort .Reverse (version .Slice (versions )))
454+ newest := versions [0 ]
455+ log .Printf ("Building package %d of %d: %s\n " , cnt , len (rebuild ), src )
456+ cnt ++
457+ result := builder .build (src , & newest )
458+ if result .err != nil {
459+ log .Printf ("building %s failed: %v\n " , src , result .err )
460+ }
461+ buildresults [src ] = result
462+
463+ if * dryRun {
464+ cmd := builder .buildCommandLine (src , & newest )
465+ dryRunBuilds = append (dryRunBuilds , dryRunBuild {
466+ Package : src ,
467+ Version : newest .String (),
468+ SbuildCommand : strings .Join (cmd , " " ),
469+ })
470+ }
471+ }
472+ return buildresults , dryRunBuilds
473+ }
474+
475+ func buildPackagesParallel (builder * sbuild , rebuild map [string ][]version.Version , numJobs int ) (map [string ]* buildResult , []dryRunBuild ) {
476+ jobs := make (chan buildJob , len (rebuild ))
477+ results := make (chan * buildResult , len (rebuild ))
478+ var wg sync.WaitGroup
479+
480+ // Start workers
481+ for i := 0 ; i < numJobs ; i ++ {
482+ wg .Add (1 )
483+ go func (workerID int ) {
484+ defer wg .Done ()
485+ for job := range jobs {
486+ log .Printf ("Worker %d building: %s\n " , workerID , job .src )
487+ result := builder .build (job .src , & job .version )
488+ if result .err != nil {
489+ log .Printf ("Worker %d: building %s failed: %v\n " , workerID , job .src , result .err )
490+ }
491+ results <- result
492+ }
493+ }(i )
494+ }
495+
496+ // Send jobs
497+ go func () {
498+ defer close (jobs )
499+ for src , versions := range rebuild {
500+ sort .Sort (sort .Reverse (version .Slice (versions )))
501+ newest := versions [0 ]
502+ jobs <- buildJob {src : src , version : newest }
503+ }
504+ }()
505+
506+ // Collect results
507+ go func () {
508+ wg .Wait ()
509+ close (results )
510+ }()
511+
512+ buildresults := make (map [string ]* buildResult )
513+ var dryRunBuilds []dryRunBuild
514+ for result := range results {
515+ buildresults [result .src ] = result
516+ if * dryRun {
517+ cmd := builder .buildCommandLine (result .src , result .version )
518+ dryRunBuilds = append (dryRunBuilds , dryRunBuild {
519+ Package : result .src ,
520+ Version : result .version .String (),
521+ SbuildCommand : strings .Join (cmd , " " ),
522+ })
523+ }
524+ }
525+
526+ log .Printf ("Completed building %d packages with %d workers\n " , len (buildresults ), numJobs )
527+ return buildresults , dryRunBuilds
528+ }
529+
432530func main () {
433531 flag .Parse ()
434532
435533 if * jsonOutput && ! * dryRun {
436534 log .Fatal ("-json can only be used together with -dry_run" )
437535 }
438536
537+ if * jobs <= 0 {
538+ log .Fatal ("-jobs must be a positive number" )
539+ }
540+
439541 if flag .NArg () == 0 {
440542 log .Fatalf ("Usage: %s [options] <path-to-changes-file>...\n " , os .Args [0 ])
441543 }
@@ -569,28 +671,15 @@ func main() {
569671 dryRun : * dryRun ,
570672 extraDebs : debs ,
571673 }
572- cnt := 1
674+
573675 buildresults := make (map [string ](* buildResult ))
574676 var dryRunBuilds []dryRunBuild
575- for src , versions := range rebuild {
576- sort .Sort (sort .Reverse (version .Slice (versions )))
577- newest := versions [0 ]
578- log .Printf ("Building package %d of %d: %s \n " , cnt , len (rebuild ), src )
579- cnt ++
580- result := builder .build (src , & newest )
581- if result .err != nil {
582- log .Printf ("building %s failed: %v\n " , src , result .err )
583- }
584- buildresults [src ] = result
585677
586- if * dryRun {
587- cmd := builder .buildCommandLine (src , & newest )
588- dryRunBuilds = append (dryRunBuilds , dryRunBuild {
589- Package : src ,
590- Version : newest .String (),
591- SbuildCommand : strings .Join (cmd , " " ),
592- })
593- }
678+ if * parallel {
679+ log .Printf ("Building packages in parallel using %d workers\n " , * jobs )
680+ buildresults , dryRunBuilds = buildPackagesParallel (builder , rebuild , * jobs )
681+ } else {
682+ buildresults , dryRunBuilds = buildPackagesSequential (builder , rebuild )
594683 }
595684
596685 var toInclude []string
0 commit comments