Three things stood between the flagship program and the web target, and each is answered here rather than worked around. The brush was a path. (rl/load-texture "brush.png") hands raylib a filename to open, and a bare relative path means nothing on a target with no filesystem. It is (embed "brush.png") now, decoded through a new binding — LoadImageFromMemory, declared (Ptr u8) plus an explicit count because the shim generator refuses a slice parameter and says so, with a Flan wrapper taking the slice apart exactly as collision-point-poly? and load-font-ex already do. One decode now serves both textures: the unflipped upload first, then ImageFlipHorizontal in place, then the mirrored one. load-texture and load-image lose their only call site in this repository; that is deliberate, because a path-based load is the thing that cannot work here. A package's C may now be addressed to one target, the way a link line already could. A .c file may carry a tag before its extension — flan_agent.web.c — and on that target it is compiled and *replaces* the untagged file of the same base name. Replacement rather than plain tagging, so that teaching a package about a new target is additive: the file that was right on three targets is not renamed to say so. Selection is in Build and not in Load, for the reason select_lflags gives. The dev agent on the web is a no-op, and the reasoning is written at length in vendor/agent/flan_agent.web.c. Short version: the agent is a socket server and a browser has no sockets, so the missing <sys/time.h> was the surface and not the cause. Refusing vendor:agent on a web target was the other candidate and is ruled out by arithmetic — Flan has no conditional compilation, sand.flan calls agent/start unconditionally, Reach cannot prune a package something reachable calls into, so a refusal means the program does not build for the browser at all. This does not contradict the `barf` decision made earlier today. `barf` is asked to make something durable, and a no-op returns success to a program that now believes bytes are on disk. The agent is asked to accept redefinitions, and on the web there is no editor, no socket and no session — --dev is refused by name on every wasm target — so there is nothing to lose. sand.flan already says the same of a native release build at the call site. test/test_web.ml builds sand.flan for the browser and reads the module for brush.png's own bytes, whole. Not "IHDR": stb_image carries that string itself, linked in from raylib, so it would pass on a build where the embed emitted nothing. It is not run — node has no DOM, so main reaches InitWindow and dies inside glfwInit on `window is not defined`, which says the module is live and nothing about whether the canvas paints. test/dune gains brush.png, because an embed is read by the checker and the headless case reaches sand.flan through ../../ from a sandboxed _build. test_session's C-c C-k case now passes ~origin, which is what both editor paths already send; omitting it was testing a request nobody makes. dune test is green. Docs follow in the next commit.
70 lines
3.6 KiB
C
70 lines
3.6 KiB
C
/* The dev agent in a browser: the three calls, doing nothing, and saying so.
|
|
*
|
|
* Build selects this file over flan_agent.c on --target=web, and only there
|
|
* (see Build.select_csrcs). The reason is not the compile error that led here
|
|
* — emscripten's headers do not pull <sys/time.h> in transitively, so
|
|
* flan_agent.c fails on `struct timeval` at line 426 — because an #include
|
|
* would have fixed that and fixed nothing real. The reason is structural:
|
|
*
|
|
* THE AGENT IS A SOCKET SERVER, AND A BROWSER HAS NO SOCKETS.
|
|
*
|
|
* flan_agent_start binds an AF_UNIX socket and hands it to a listener thread.
|
|
* There is no such address family under emscripten, nothing to listen on, and
|
|
* nothing that could connect if there were. Adding the include produces an
|
|
* agent that compiles, links, starts, and can never accept a connection.
|
|
*
|
|
* ── Why a no-op here, when `barf` on the web signals instead ────────────
|
|
*
|
|
* NEXT.md decision 2 rejected a silent no-op for `barf` in as many words: a
|
|
* no-op write is how a save file disappears with nothing said. The two look
|
|
* contradictory and are not, and the difference is what the caller loses.
|
|
*
|
|
* `barf` is asked to make something durable. A no-op returns success to a
|
|
* program that now believes the bytes are on disk; the loss is real, it is the
|
|
* user's, and it is discovered later or never. So the web signals a condition
|
|
* and the program decides.
|
|
*
|
|
* The agent is asked to accept redefinitions from an editor. On the web there
|
|
* is no editor, no socket, and no session — `--dev` is refused by name on
|
|
* every wasm target, so a web build has no cells to install a redefinition
|
|
* into even if one arrived. There is nothing to lose because there was never
|
|
* anything there. sand.flan already says the same thing about a *native*
|
|
* release build, at the call site:
|
|
*
|
|
* "Building without --dev is fine — nothing has cells to install into, so a
|
|
* module is refused on the listener thread and the loop never notices."
|
|
*
|
|
* A web build reaches that same outcome by a shorter route. The no-op is not
|
|
* hiding a failure; it is the truthful implementation of "nothing is
|
|
* available here", which is the case where Odin's own `.Unsupported` stubs are
|
|
* right and the case decision 2 was careful to say it was not.
|
|
*
|
|
* ── Why not refuse vendor:agent on a web target ─────────────────────────
|
|
*
|
|
* It was the other candidate and it is ruled out by arithmetic, not taste.
|
|
* Flan has no conditional compilation, so a program cannot say "skip this on
|
|
* web". sand.flan calls (agent/start ...) unconditionally, Reach cannot prune
|
|
* a package something reachable calls into, and a build-time refusal would
|
|
* therefore mean sand.flan does not build for the browser at all without being
|
|
* edited into a second program. Refusing is only honest when the caller has a
|
|
* way to not ask; here it has none.
|
|
*
|
|
* ── What the return values say ──────────────────────────────────────────
|
|
*
|
|
* start returns -1, which is the same "could not listen" flan_agent.c returns
|
|
* for a path it cannot bind, so a caller that checks gets the answer it
|
|
* already knows how to read. poll and wait return 0 — no redefinitions
|
|
* installed — which is the truth and is what the native build returns on every
|
|
* frame that nothing arrived on. */
|
|
|
|
#include <stdint.h>
|
|
|
|
int32_t flan_agent_start(const uint8_t *path, int64_t len) {
|
|
(void)path; (void)len;
|
|
return -1;
|
|
}
|
|
|
|
int32_t flan_agent_poll(void) { return 0; }
|
|
|
|
int32_t flan_agent_wait(int32_t ms) { (void)ms; return 0; }
|