@@ -21,9 +21,12 @@ import (
2121 "os/exec"
2222 "path/filepath"
2323 "regexp"
24+ "runtime"
2425 "sort"
2526 "strings"
27+ "sync"
2628
29+ "golang.org/x/sync/errgroup"
2730 "pault.ag/go/debian/control"
2831 "pault.ag/go/debian/version"
2932)
@@ -100,6 +103,14 @@ var (
100103 false ,
101104 "Output results in JSON format (currently only works in combination with -dry_run)" )
102105
106+ parallel = flag .Bool ("parallel" ,
107+ false ,
108+ "Build packages in parallel" )
109+
110+ jobs = flag .Int ("jobs" ,
111+ runtime .NumCPU (),
112+ "Number of parallel build jobs (default: number of CPU cores)" )
113+
103114 listsPrefixRe = regexp .MustCompile (`/([^/]*_dists_.*)_InRelease$` )
104115)
105116
@@ -429,13 +440,91 @@ func getAptIndexPaths(dist string) ([]string, []string) {
429440 return fallbackIndexPaths ()
430441}
431442
443+ type buildJob struct {
444+ src string
445+ version version.Version
446+ }
447+
448+ func buildPackagesSequential (builder * sbuild , rebuild map [string ][]version.Version ) (map [string ]* buildResult , []dryRunBuild ) {
449+ buildresults := make (map [string ]* buildResult )
450+ var dryRunBuilds []dryRunBuild
451+ cnt := 1
452+
453+ for src , versions := range rebuild {
454+ sort .Sort (sort .Reverse (version .Slice (versions )))
455+ newest := versions [0 ]
456+ log .Printf ("Building package %d of %d: %s\n " , cnt , len (rebuild ), src )
457+ cnt ++
458+ result := builder .build (src , & newest )
459+ if result .err != nil {
460+ log .Printf ("building %s failed: %v\n " , src , result .err )
461+ }
462+ buildresults [src ] = result
463+
464+ if * dryRun {
465+ cmd := builder .buildCommandLine (src , & newest )
466+ dryRunBuilds = append (dryRunBuilds , dryRunBuild {
467+ Package : src ,
468+ Version : newest .String (),
469+ SbuildCommand : strings .Join (cmd , " " ),
470+ })
471+ }
472+ }
473+ return buildresults , dryRunBuilds
474+ }
475+
476+ func buildPackagesParallel (builder * sbuild , rebuild map [string ][]version.Version , numJobs int ) (map [string ]* buildResult , []dryRunBuild ) {
477+ var eg errgroup.Group
478+ eg .SetLimit (numJobs )
479+
480+ buildresults := make (map [string ]* buildResult )
481+ var dryRunBuilds []dryRunBuild
482+ var resultsMu sync.Mutex
483+ cnt := 1
484+
485+ for src , versions := range rebuild {
486+ src , versions := src , versions // capture loop variables
487+ eg .Go (func () error {
488+ sort .Sort (sort .Reverse (version .Slice (versions )))
489+ newest := versions [0 ]
490+ log .Printf ("Building package %d of %d: %s\n " , cnt , len (rebuild ), src )
491+ cnt ++
492+ result := builder .build (src , & newest )
493+ if result .err != nil {
494+ log .Printf ("building %s failed: %v\n " , src , result .err )
495+ }
496+
497+ resultsMu .Lock ()
498+ buildresults [src ] = result
499+ if * dryRun {
500+ cmd := builder .buildCommandLine (src , & newest )
501+ dryRunBuilds = append (dryRunBuilds , dryRunBuild {
502+ Package : src ,
503+ Version : newest .String (),
504+ SbuildCommand : strings .Join (cmd , " " ),
505+ })
506+ }
507+ resultsMu .Unlock ()
508+ return nil
509+ })
510+ }
511+
512+ eg .Wait ()
513+ log .Printf ("Completed building %d packages with %d workers\n " , len (buildresults ), numJobs )
514+ return buildresults , dryRunBuilds
515+ }
516+
432517func main () {
433518 flag .Parse ()
434519
435520 if * jsonOutput && ! * dryRun {
436521 log .Fatal ("-json can only be used together with -dry_run" )
437522 }
438523
524+ if * jobs <= 0 {
525+ log .Fatal ("-jobs must be a positive number" )
526+ }
527+
439528 if flag .NArg () == 0 {
440529 log .Fatalf ("Usage: %s [options] <path-to-changes-file>...\n " , os .Args [0 ])
441530 }
@@ -569,28 +658,15 @@ func main() {
569658 dryRun : * dryRun ,
570659 extraDebs : debs ,
571660 }
572- cnt := 1
661+
573662 buildresults := make (map [string ](* buildResult ))
574663 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
585664
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- }
665+ if * parallel {
666+ log .Printf ("Building packages in parallel using %d workers\n " , * jobs )
667+ buildresults , dryRunBuilds = buildPackagesParallel (builder , rebuild , * jobs )
668+ } else {
669+ buildresults , dryRunBuilds = buildPackagesSequential (builder , rebuild )
594670 }
595671
596672 var toInclude []string
0 commit comments