Write generated/generator.json only after message generation completes - #1595
Conversation
generateAll() copied the version marker into generated/ before the generateInPath() loop, so a run that was killed part-way or failed on one package left a tree that init() treated as complete and every missing interface raised MESSAGE_NOT_FOUND until generated/ was deleted by hand. Write the marker last so a partial tree has no generator.json and init() regenerates it on the next start. Make scripts/generate_messages.cjs exit non-zero on error so npm install does not report success for a partial tree. Add a regression test. Fix: RobotWebTools#1594
KR-Ravindra
left a comment
There was a problem hiding this comment.
Round 1 self-review.
Checked against develop: rosidl_gen/index.cjs copies generator.json into generated/ before the generateInPath() loop, init() only regenerates when that marker is missing or older than the generator, and scripts/generate_messages.cjs logs the error and returns normally, so the description matches the code.
- Marker is now written after the loop and
generated/is created withfse.mkdirs()up front (the copy used to create it implicitly), so an interrupted or failed run leaves no marker and the nextinit()regenerates. Looks correct. process.exit(1)in the postinstall catch makesnpm installreport the failure instead of exiting 0 with a partial tree.- The new mocha case uses the file's existing
generateMessages()/GENERATED_PATHhelpers, restoresAMENT_PREFIX_PATHand removes the temp prefix infinally.
CI: full ROS 2 matrix (humble, jazzy, kilted, lyrical, rolling; arm64 + x64; pixi builds) green, coverage unchanged. Marking ready.
There was a problem hiding this comment.
🟢 Approval recommended
The change directly addresses the reported partial-generation failure mode and adds a targeted regression test, with only a minor logging improvement suggested.
Pull request overview
This PR hardens ROS message generation so a partially generated generated/ tree is no longer treated as complete by rclnodejs.init(), and generation failures correctly surface as non-zero exits (preventing silent broken installs).
Changes:
- Delay writing
generated/generator.jsonuntil after all interfaces finish generating, so interrupted/failed runs leave no “complete” marker. - Make
scripts/generate_messages.cjsexit with status 1 on generation errors (sonpm install/npx generate-ros-messagesfail loudly). - Add a regression test ensuring failed generation does not leave
generated/generator.jsonbehind.
File summaries
| File | Description |
|---|---|
rosidl_gen/index.cjs |
Moves the version marker write to the end of successful generation and ensures generated/ exists before writing other outputs. |
scripts/generate_messages.cjs |
Ensures message generation failures propagate as a non-zero process exit. |
test/test-messsage-generation-overlay.js |
Adds a regression test for the “no marker after failed generation” behavior. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| } catch (e) { | ||
| console.log(`Caught error: ${e}`); | ||
| process.exit(1); | ||
| } |
|
lgtm, merging |
Problem
If message generation does not run to completion,
rclnodejs.init()keeps treating the partialgenerated/tree as complete. Every interface that was never written then fails withMESSAGE_NOT_FOUNDon every start, and the only way out is to deletegenerated/by hand. The two realistic ways to get there are the generating process being killed part-way (SIGKILL/OOM duringnpm install, Ctrl-C ongenerate-ros-messages, CI/container teardown) and any single package failing to generate (EACCES, ENOSPC, a malformed interface). In the second casenpm installalso exits 0, because thepostinstallscript only logs the error, so the partial tree is left behind silently.Root cause
rosidl_gen/index.cjs:87-90copiesgenerator.json(the version markerinit()uses to decide whether regeneration is needed) intogenerated/before the per-prefixgenerateInPath()loop atrosidl_gen/index.cjs:103-105has generated a single interface file.index.js:422-424(getCurrentGeneratorVersion()/forced) only regenerates when that file is absent or carries an older version, so a partial tree with a current-version marker is never regenerated.scripts/generate_messages.cjs:36-38catches the error, printsCaught error: ...and returns normally, so the process exits 0.Fix
rosidl_gen/index.cjs: writegenerated/generator.jsonlast, after thegenerateInPath()loop.generated/is now created withfse.mkdirs()up front (thegenerator.jsoncopy used to create it implicitly). A tree left behind by an interrupted or failed run has no marker,getCurrentGeneratorVersion()returnsnull, andinit()regenerates it on the next start with no other change.scripts/generate_messages.cjs:process.exit(1)in the catch handler sonpm installandnpx generate-ros-messagesreport the failure instead of succeeding with a partial tree.test/test-messsage-generation-overlay.js: regression test that pointsAMENT_PREFIX_PATHat a prefix whose ament index lists a.msgfile that does not exist, runsgenerateAll(true), and asserts that it fails and that nogenerated/generator.jsonwas written.How tested
New test against the unmodified generator:
With the fix:
Also checked that a successful
generateAll(false)followed bygenerateAll(true)still producesgenerated/generator.json,generated/package.jsonandgenerated/srv_msg/, and thatnode scripts/generate_messages.cjsnow exits 1 when a package fails to generate (generated/is left withoutgenerator.json).npx prettier --checkandnpx eslintpass on the changed files.Links
This change was prepared with an AI agent operated by KR-Ravindra, who reviewed and tested it.