Skip to content

change prototype of onBuild, onTest#161

Open
xushiwei wants to merge 2 commits into
xgo-dev:mainfrom
xushiwei:q
Open

change prototype of onBuild, onTest#161
xushiwei wants to merge 2 commits into
xgo-dev:mainfrom
xushiwei:q

Conversation

@xushiwei

Copy link
Copy Markdown
Contributor

No description provided.

@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: change prototype of onBuild, onTest

The signature simplification (func(ctx *Context) with build state moved onto Context) is clean and consistently propagated through classfile.go, internal/formula/formula.go, build.go, the Go tests, and the zlib demo. However there are a few concrete correctness problems that look like they would fail the build/tests as-is.

Blocking

1. 57 .gox formula files still use the old 3-arg signature — only the zlib demo was migrated.
This PR changes the DSL hook prototype to onBuild ctx => { ... } and drops out, but only formula/demo/zlib/zlib_llar.gox was updated. 57 other .gox files still use onBuild (ctx, proj, out) => / onBuild (proj, out) => with out.addErr / out.setMetadata / installDir, err := ctx.outputDir(). Many are active test fixtures that the loaders type-assert against the new signature — e.g.:

  • internal/formula/testdata/formula/targetsurface_llar.gox (loaded by TestLoadFS_TargetSurface, TestClone, TestFormulaProgramCleanup)
  • internal/build/testdata/formulas/test/liba/1.0.0/Liba_llar.gox and siblings (loaded across internal/build tests)
  • cmd/llar/internal/testdata/formulas/**

internal/formula/formula.go does valueOf(class, "fOnBuild").(func(*formula.Context)); a fixture compiled with the old arity will either fail to compile against the new OnBuild gopt or panic on that type assertion. These fixtures must be migrated in the same PR, otherwise the test suite breaks. (grep -rl 'onBuild (ctx, proj\|onBuild (proj\|out.addErr\|out.setMetadata\|ctx.outputDir()' --include='*.gox' → 57 files.)

2. errors.Is no longer reaches the wrapped cause — breaks the existing OnTest test.
See inline comment on internal/build/build.go. buildContext.Errs is github.com/qiniu/x/errors.List (type List []error), which has no Unwrap(). The old code wrapped errors.Join(testOut.Errs()...) (which errors.Is can walk); the new %w-wrap of List cannot be traversed, so the unchanged errors.Is(err, wantErr) assertion in TestBuild_RunTest_EnabledSurfacesOnTestError (build_test.go:956) will fail.

Should fix

3. OutputDir__1 panics across a non-recovering boundary.
See inline comment on formula/project.go. There is no recover() anywhere in the repo, and on the HTTP build path Builder.Build runs inside a singleflight.DoChan goroutine (internal/build/http/http.go:107), where an unrecovered panic crashes the whole server process rather than surfacing as a build error. The c.Errs.Add(err) before panic(err) is also dead — nothing reads Errs once the panic unwinds.

Minor

  • Shared-context error attribution (internal/build/build.go:305, :316): OnBuild and OnTest now write into the same buildContext.Errs, and both phases test len(buildContext.Errs) > 0. It works today only because OnBuild returns early on error. If a future change lets control reach OnTest with pre-existing errors (or OutputDir__1 records without panicking), a build-phase error would be mis-reported as "onTest failed". Consider snapshotting the length before OnTest, or a per-phase list.
  • Stale docs not updated with the new signature: AGENTS.md:64 (onBuild (ctx, proj, out) =>), AGENTS.md:88 (this.OnBuild(func(ctx *formula.Context, proj *formula.Project, out *formula.BuildResult)), and doc/formula.md:24 (onBuild (proj, out) =>).

Comment thread internal/build/build.go
return fmt.Errorf("onTest failed for %s@%s: %w", mod.Path, mod.Version, errors.Join(testOut.Errs()...))
mod.OnTest(buildContext)
if len(buildContext.Errs) > 0 {
return fmt.Errorf("onTest failed for %s@%s: %w", mod.Path, mod.Version, buildContext.Errs)

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.

buildContext.Errs is qiniu/x/errors.List (type List []error), which does not implement Unwrap(). Wrapping it with %w produces a chain that errors.Is cannot descend past, so errors.Is(err, cause) no longer finds the injected error.

The unchanged assertion in TestBuild_RunTest_EnabledSurfacesOnTestError (build_test.go:956) — if !errors.Is(err, wantErr) — will fail with this change. The previous code used errors.Join(testOut.Errs()...), which yields an Unwrap() []error chain that errors.Is can walk.

Suggest wrapping buildContext.Errs.ToError() (returns the single error directly when len == 1) or keeping errors.Join(buildContext.Errs...) so the cause stays inspectable.

Comment thread formula/project.go
dir, err := c.getOutputDir(c.matrixStr, mod)
if err != nil {
c.Errs.Add(err)
panic(err)

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 concerns with panic(err) here:

  1. Unrecovered panic can crash the process. OnBuild/OnTest run inside execbroker.Do(...) in internal/build/build.go, and there is no recover() anywhere in the repo. ctx.outputDir(dep) can legitimately fail (e.g. an undeclared dependency), so this panics on a routine, input-triggerable error. On the HTTP build path Builder.Build runs inside a singleflight.DoChan goroutine (internal/build/http/http.go:107) — a panic there is not recovered by net/http and takes down the whole server. Prefer recording into c.Errs and returning "" (the existing len(buildContext.Errs) > 0 check then surfaces it), or add a recover boundary around the hook invocation.
  2. Dead Errs.Add. Because the panic is never recovered, the c.Errs.Add(err) on the line above is never read. Either surface via Errs and return, or panic without the redundant Add.

Also: the doc comment (// OutputDir__1 returns ... // In DSL: ctx.outputDir(dep)) no longer mentions that this now panics on failure and drops the error return — worth documenting for formula authors.

Comment thread formula/project.go

// NewContext creates a Context with build-internal fields.
func NewContext(sourceDir, installDir, matrixStr string, getOutputDir func(string, module.Version) (string, error)) *Context {
func NewContext(proj *Project, sourceDir, installDir, matrixStr string, getOutputDir func(string, module.Version) (string, error)) *Context {

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 exported NewContext lost its doc comment (// NewContext creates a Context with build-internal fields.) in this change and now gains a new proj parameter. Please restore a doc comment (mentioning the new proj) to keep the exported API documented.

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