-
Couldn't load subscription status.
- Fork 16
Add parallel build support with -parallel and -jobs options #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,13 @@ | ||
| module github.com/Debian/ratt | ||
|
|
||
| go 1.18 | ||
| go 1.24.0 | ||
|
|
||
| toolchain go1.24.4 | ||
|
|
||
| require pault.ag/go/debian v0.12.0 | ||
|
|
||
| require ( | ||
| golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect | ||
| golang.org/x/sync v0.17.0 // indirect | ||
| pault.ag/go/topsort v0.0.0-20160530003732-f98d2ad46e1a // indirect | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,9 +21,12 @@ import ( | |
| "os/exec" | ||
| "path/filepath" | ||
| "regexp" | ||
| "runtime" | ||
| "sort" | ||
| "strings" | ||
| "sync" | ||
|
|
||
| "golang.org/x/sync/errgroup" | ||
| "pault.ag/go/debian/control" | ||
| "pault.ag/go/debian/version" | ||
| ) | ||
|
|
@@ -100,6 +103,14 @@ var ( | |
| false, | ||
| "Output results in JSON format (currently only works in combination with -dry_run)") | ||
|
|
||
| parallel = flag.Bool("parallel", | ||
| false, | ||
| "Build packages in parallel") | ||
|
|
||
| jobs = flag.Int("jobs", | ||
| runtime.NumCPU(), | ||
| "Number of parallel build jobs (default: number of CPU cores)") | ||
|
|
||
| listsPrefixRe = regexp.MustCompile(`/([^/]*_dists_.*)_InRelease$`) | ||
| ) | ||
|
|
||
|
|
@@ -429,13 +440,91 @@ func getAptIndexPaths(dist string) ([]string, []string) { | |
| return fallbackIndexPaths() | ||
| } | ||
|
|
||
| type buildJob struct { | ||
| src string | ||
| version version.Version | ||
| } | ||
|
|
||
| func buildPackagesSequential(builder *sbuild, rebuild map[string][]version.Version) (map[string]*buildResult, []dryRunBuild) { | ||
| buildresults := make(map[string]*buildResult) | ||
| var dryRunBuilds []dryRunBuild | ||
| cnt := 1 | ||
|
|
||
| for src, versions := range rebuild { | ||
| sort.Sort(sort.Reverse(version.Slice(versions))) | ||
| newest := versions[0] | ||
| log.Printf("Building package %d of %d: %s\n", cnt, len(rebuild), src) | ||
| cnt++ | ||
| result := builder.build(src, &newest) | ||
| if result.err != nil { | ||
| log.Printf("building %s failed: %v\n", src, result.err) | ||
| } | ||
| buildresults[src] = result | ||
|
|
||
| if *dryRun { | ||
| cmd := builder.buildCommandLine(src, &newest) | ||
| dryRunBuilds = append(dryRunBuilds, dryRunBuild{ | ||
| Package: src, | ||
| Version: newest.String(), | ||
| SbuildCommand: strings.Join(cmd, " "), | ||
| }) | ||
| } | ||
| } | ||
| return buildresults, dryRunBuilds | ||
| } | ||
|
|
||
| func buildPackagesParallel(builder *sbuild, rebuild map[string][]version.Version, numJobs int) (map[string]*buildResult, []dryRunBuild) { | ||
| var eg errgroup.Group | ||
| eg.SetLimit(numJobs) | ||
|
|
||
| buildresults := make(map[string]*buildResult) | ||
| var dryRunBuilds []dryRunBuild | ||
| var resultsMu sync.Mutex | ||
| cnt := 1 | ||
|
|
||
| for src, versions := range rebuild { | ||
| src, versions := src, versions // capture loop variables | ||
| eg.Go(func() error { | ||
| sort.Sort(sort.Reverse(version.Slice(versions))) | ||
| newest := versions[0] | ||
| log.Printf("Building package %d of %d: %s\n", cnt, len(rebuild), src) | ||
| cnt++ | ||
| result := builder.build(src, &newest) | ||
| if result.err != nil { | ||
| log.Printf("building %s failed: %v\n", src, result.err) | ||
| } | ||
|
|
||
| resultsMu.Lock() | ||
| buildresults[src] = result | ||
| if *dryRun { | ||
| cmd := builder.buildCommandLine(src, &newest) | ||
| dryRunBuilds = append(dryRunBuilds, dryRunBuild{ | ||
| Package: src, | ||
| Version: newest.String(), | ||
| SbuildCommand: strings.Join(cmd, " "), | ||
| }) | ||
| } | ||
| resultsMu.Unlock() | ||
| return nil | ||
| }) | ||
| } | ||
|
|
||
| eg.Wait() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While currently, the function we run on the errgroup never returns an error, please add error handling for this |
||
| log.Printf("Completed building %d packages with %d workers\n", len(buildresults), numJobs) | ||
| return buildresults, dryRunBuilds | ||
| } | ||
|
|
||
| func main() { | ||
| flag.Parse() | ||
|
|
||
| if *jsonOutput && !*dryRun { | ||
| log.Fatal("-json can only be used together with -dry_run") | ||
| } | ||
|
|
||
| if *jobs <= 0 { | ||
| log.Fatal("-jobs must be a positive number") | ||
| } | ||
|
|
||
| if flag.NArg() == 0 { | ||
| log.Fatalf("Usage: %s [options] <path-to-changes-file>...\n", os.Args[0]) | ||
| } | ||
|
|
@@ -569,28 +658,15 @@ func main() { | |
| dryRun: *dryRun, | ||
| extraDebs: debs, | ||
| } | ||
| cnt := 1 | ||
|
|
||
| buildresults := make(map[string](*buildResult)) | ||
| var dryRunBuilds []dryRunBuild | ||
| for src, versions := range rebuild { | ||
| sort.Sort(sort.Reverse(version.Slice(versions))) | ||
| newest := versions[0] | ||
| log.Printf("Building package %d of %d: %s \n", cnt, len(rebuild), src) | ||
| cnt++ | ||
| result := builder.build(src, &newest) | ||
| if result.err != nil { | ||
| log.Printf("building %s failed: %v\n", src, result.err) | ||
| } | ||
| buildresults[src] = result | ||
|
|
||
| if *dryRun { | ||
| cmd := builder.buildCommandLine(src, &newest) | ||
| dryRunBuilds = append(dryRunBuilds, dryRunBuild{ | ||
| Package: src, | ||
| Version: newest.String(), | ||
| SbuildCommand: strings.Join(cmd, " "), | ||
| }) | ||
| } | ||
| if *parallel { | ||
| log.Printf("Building packages in parallel using %d workers\n", *jobs) | ||
| buildresults, dryRunBuilds = buildPackagesParallel(builder, rebuild, *jobs) | ||
| } else { | ||
| buildresults, dryRunBuilds = buildPackagesSequential(builder, rebuild) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| var toInclude []string | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this line, capturing loop variables has not been necessary since Go 1.22 (https://go.dev/blog/loopvar-preview).
By the way, your editor should show you a warning about this and offer a code action to remove the line. If you don’t see this, maybe you use an old version of gopls, or something is wrong with your editor or IDE’s LSP setup…?