Skip to content

Pack the wasm archive somewhere a consumer can link it - #12

Merged
jcant0n merged 2 commits into
mainfrom
wasm-that-can-actually-be-linked
Aug 8, 2026
Merged

Pack the wasm archive somewhere a consumer can link it#12
jcant0n merged 2 commits into
mainfrom
wasm-that-can-actually-be-linked

Conversation

@jcant0n

@jcant0n jcant0n commented Aug 8, 2026

Copy link
Copy Markdown
Member

The package has been shipping 3.6 MB of browser-wasm that nobody could have used. Two independent mistakes had to line up:

  • The archive was packed as libJoltC.a while every DllImport says "JoltC". On wasm the module name comes from the file name, so the lib prefix alone breaks resolution.
  • No buildTransitive targets file, which matters more. runtimes/browser-wasm/native is never probed — wasm has no dynamic loading, so the archive has to be linked in at publish time via NativeFileReference. Without one it was not linked badly, it was not linked at all.

Nothing could have caught it. The eleven C suites in JoltPhysicsC test the library, not the package. The CD proves the package builds. The desktop legs load out of runtimes/ at run time, which is precisely the mechanism wasm does not use. It surfaced by opening the published .nupkg to check something else.

The archive was fine all along

Before changing anything I renamed the published .a to JoltC.a and linked it into a real .NET wasm application. It worked:

JoltC on browser-wasm
  allocator=0x7e7300 jobSystem=0x7e6dc0
  sphere went from y=2.000 to y=0.486 in 63 steps
PASS

None of what made Cesium's wasm hard is present — no setjmp in any form, no pthread, no JS exception model — so no emcc flags change in JoltPhysicsC, and the contingency about porting JobSystemSingleThreaded is moot: numThreads = 0 is enough.

And the leg that would have caught it

WasmSmokeTest consumes the package through PackageReference, so the targets file and the packed file name are part of what is under test. A ProjectReference bypasses both and would pass against the package this PR fixes.

It is its own job rather than a matrix row: no identifier to select, a workload to install, and node rather than the host to run the result. Verified locally against a package packed from this branch:

  ok    the allocator and job system were created, so the archive linked and the DllImports resolved
  ok    the physics system was created
  sphere went from y=2.000 to y=0.486 in 63 steps
  ok    the sphere came to rest in 63 steps, so the simulation both ran and settled
  ok    the sphere fell, so gravity was integrated
  ok    it settled on the floor rather than falling through it (y=0.486)
PASS

One difference from Cesium worth noting: there the ten natives are fetched at CD time, so its equivalent block is conditioned on the file existing. Here they are committed, so a missing archive means a bad fetch and packing should fail loudly instead — the condition is deliberately absent.

The package has been shipping 3.6 MB of browser-wasm that nobody could
have used, and two independent mistakes had to line up for that:

The archive was packed as libJoltC.a while every DllImport in Generated
says "JoltC". On wasm the module name comes from the file name, so the
lib prefix alone breaks resolution.

And there was no buildTransitive targets file, which matters more.
runtimes/browser-wasm/native is never probed, because wasm has no
dynamic loading: the archive has to be linked into the application when
it publishes, through a NativeFileReference. Without one the archive was
not linked badly, it was not linked at all. The csproj had a single
Content Include of runtimes/** that carried it to a decorative path.

Nothing in either repository could have caught it. The eleven C test
suites in JoltPhysicsC exercise the library, not the package; the CD
proves the package builds; the desktop legs of the smoke test load
libraries out of runtimes/ at run time, which is exactly the mechanism
wasm does not use. It surfaced by opening the published .nupkg to check
something else.

The archive itself turned out to be fine. Before changing anything I
renamed the published one to JoltC.a and linked it into a real .NET wasm
application: wasm-ld accepted it, wasm-opt validated it, and the sphere
fell and settled under node. None of what made Cesium's wasm hard is
present here -- no setjmp in any form, no pthread, no JS exception
model -- so no emcc flags change in JoltPhysicsC.

WasmSmokeTest goes through PackageReference, so the targets file and the
packed file name are part of what it tests; a ProjectReference would
bypass both and pass against the package this commit fixes. It runs as
its own job in the smoke workflow rather than a matrix row, because it
shares almost nothing with the desktop legs: no identifier to select,
a workload to install, and node rather than the host to run the result.

Two deliberate differences from the same scenario on desktop. The job
system is created with no worker threads, since there is no pthread
here and JoltPhysics then runs jobs on the thread waiting at the
barrier. And the bounds on the result are looser: the same drop settles
in 63 steps at y=0.486 here against 48 at y=0.480 on the desktop legs,
so asserting the desktop numbers would fail for a reason that has
nothing to do with the package.
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

API gate: additive

Every symbol that existed still exists, unchanged. Nothing that compiled before stops compiling.

Target framework Added Removed
net10.0 +0 -0

Enum and constant values are part of the measured surface: a renumbering keeps compiling and sends the wrong number to the driver, so it counts as a removal.

It selects desktop identifiers only, and once wasm became a separate job
"all five" was ambiguous about whether it covered everything.
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

API gate: additive

Every symbol that existed still exists, unchanged. Nothing that compiled before stops compiling.

Target framework Added Removed
net10.0 +0 -0

Enum and constant values are part of the measured surface: a renumbering keeps compiling and sends the wrong number to the driver, so it counts as a removal.

@jcant0n
jcant0n merged commit 9a91a8e into main Aug 8, 2026
6 checks passed
@jcant0n
jcant0n deleted the wasm-that-can-actually-be-linked branch August 8, 2026 12:23
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