Skip to content

build: plan package dependency levels - #2177

Closed
zhouguangyuan0718 wants to merge 10 commits into
xgo-dev:mainfrom
zhouguangyuan0718:agent/parallel-build-pr3
Closed

build: plan package dependency levels#2177
zhouguangyuan0718 wants to merge 10 commits into
xgo-dev:mainfrom
zhouguangyuan0718:agent/parallel-build-pr3

Conversation

@zhouguangyuan0718

Copy link
Copy Markdown
Contributor

Summary

  • build an immutable package dependency plan from effective dependencies
  • derive stable ready levels for a future bounded worker scheduler
  • validate duplicate package IDs and dependency cycles without changing the current serial execution order

Validation

  • GOCACHE=/private/tmp/llgo-pr3-gocache go test ./internal/build ./internal/goflags ./cmd/internal/flags -count=1

Dependency

This PR includes and depends on #2174, #2175, and #2176. Merge/rebase it after those PRs; it will then contain only this dependency-plan stage.

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary

This is a clean, well-staged refactor toward bounded parallel compilation. The extraction of packageBuildSpec/packageBuildResult, the preflight/execute/finalize split, and the register-then-build separation for SSA packages are clear and well-tested. The llssaInitOnce guard and eager initializePackageBuildState are good defensive moves ahead of parallelism, and effectiveDependencies correctly closes a latent stale-cache hole (alt-package imports were previously not fingerprinted). The concurrent SSA worker pool in buildSSAPkgs is correct: worker count is bounded, the channel/WaitGroup pattern is leak-free, packages are de-duplicated, and fixSSAOrder is correctly kept serial.

A few points worth addressing (details inline):

  • Scaffolding computed on every build but unused in production. newPackageBuildPlan builds deps, byID, and the topological levels (internal/build/package_build.go:84-175), but buildAllPkgs consumes only plan.specs; levels/deps/byID are read only from tests. This is intentional per the PR title, but the full O(V+E) leveling + allocations run on every build for a result nothing consumes yet. Consider documenting that levels does not yet drive execution, or deferring it until the consuming scheduler lands, so future readers don't assume it controls build order. Similarly packageBuildResult.spec/archiveFile/cacheHit are currently write-only in production.

  • Data-race caveat for the follow-up executor. initializePackageBuildState eagerly initializes cacheManager/llvmVersion, but ctx.built (written in preflightPackageBuild) and ctx.sfilesCache remain plain maps with unguarded read/write. They are safe only because the package pipeline is still serial today; they must be guarded or sharded before buildOnePackage/the backend is parallelized. Worth a note so it isn't overlooked.

No blocking issues.

Comment thread internal/build/package_build.go Outdated
count += len(level)
}
if count != len(p.specs) {
return nil, fmt.Errorf("package build dependency cycle")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cycle error names no packages, making it hard to diagnose. Since effectiveDependencies now merges alt-package imports into the edge set, an alt package that (transitively) imports the package it patches can form a cycle that previously never failed the build — and this error is reachable from production Do() even though levels is otherwise unused. Consider including the involved package IDs in the message so a real regression here is diagnosable, and confirm no existing alt-package arrangement in the tree trips it.

continue
}
parallel, err = strconv.Atoi(value)
if err != nil || parallel <= 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two minor points on -p validation:

  1. No upper bound: any positive int is accepted (e.g. -p=2147483647). The SSA worker count is capped by min(parallelism, len(unique)) so goroutines are bounded by package count, but the value is also forwarded to go/packages uncapped. A defensive clamp (e.g. to runtime.NumCPU() or a fixed ceiling) would close the theoretical resource-exhaustion angle.
  2. -p=0 is rejected here, while Config.Parallel == 0 is documented as valid ("Zero uses GOMAXPROCS") and accepted by Do. The two entry points disagree on whether 0 is legal. Rejecting 0 at the CLI is defensible (Go does too), but a one-line comment noting this would avoid confusion.

@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@zhouguangyuan0718
zhouguangyuan0718 force-pushed the agent/parallel-build-pr3 branch 2 times, most recently from 901065d to da465b3 Compare July 26, 2026 08:14
@zhouguangyuan0718
zhouguangyuan0718 force-pushed the agent/parallel-build-pr3 branch from da465b3 to f66d10e Compare July 27, 2026 02:47
@zhouguangyuan0718
zhouguangyuan0718 force-pushed the agent/parallel-build-pr3 branch from f66d10e to 697eb43 Compare July 27, 2026 04:35
@zhouguangyuan0718

Copy link
Copy Markdown
Contributor Author

Closing as obsolete after the architecture review: LLVM package backends no longer wait on dependency levels. The dependency-DAG/ready-level scheduler has been removed from the consolidated implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant