build: keep the linker options of a gem that is in libmruby.a - #7203
Conversation
`MRuby::Gem::List#linker_attrs` drops every gem that builds a binary, and hands the rest to each link of libmruby.a. 5c130e8 added that for mruby#5210, where a gem's options reached binaries the gem had nothing to do with. It reads `bin?` as "not a library gem", and a gem can be both. `build.libmruby_objs << @objs` (lib/mruby/gem.rb:103) is unconditional, so a gem with a src/ or an mrblib/ puts objects in libmruby.a whatever else it builds. When such a gem also declares `spec.bins`, its objects are in the archive and its linker options are not on the line that links it, so anything but its own binary fails to link. `mruby-task` is that gem as soon as `MRB_TASK_BUILD_DEMO` is set: the define gives it `spec.bins` (mrbgems/mruby-task/mrbgem.rake:52), and it carries src/ and a ports/<port>/ HAL whose glib port is what needs the `search_package` libraries. build_config/glib_hal_test.rb sets both, and mrbtest cannot be linked there: $ MRUBY_CONFIG=glib_hal_test rake -m test ld: mrbgems/mruby-task/ports/glib/task_hal.c:132: undefined reference to `g_main_loop_quit' ld: mrbgems/mruby-task/ports/glib/task_hal.c:134: undefined reference to `g_thread_join' ... rake aborted! Tasks: TOP => bin/mrbtest => build/host/bin/mrbtest gcc -fsanitize=address,undefined -o "build/host/bin/mrbtest" .../driver.o .../vformat.o .../mrbtest.o .../mrbtest.a "build/host/lib/libmruby.a" -lm Drop a binary gem only when it contributes no object. A gem that put nothing in libmruby.a still has options that belong to its own binary alone, which is what mruby#5210 asked for, and one that did needs them wherever libmruby.a is linked: $ MRUBY_CONFIG=glib_hal_test rake -m test # exit 0 bintest - Command Binary Test Total: 0 OK: 0 KO: 0 Crash: 0 Skip: 0 mrbtest - Embeddable Ruby Test Total: 832 OK: 831 KO: 0 Crash: 0 Skip: 1 gcc -fsanitize=address,undefined -o "build/host/bin/mrbtest" .../driver.o .../vformat.o .../mrbtest.o .../mrbtest.a "build/host/lib/libmruby.a" -L/usr/lib/x86_64-linux-gnu -lglib-2.0 -L/usr/lib/x86_64-linux-gnu -lgthread-2.0 -pthread -lglib-2.0 -lm Those libraries appear twice because the gem calls `search_package` twice, before and after this commit alike. The second line follows from the first. tasks/bin.rake:10 asks for `linker_attrs(gem)` when it links a gem's own binary, and a gem that now stays in the library set would be appended to it a second time, so its options would be doubled on that line. Append it only when it is not already there. build/host/bin/mruby_task_demo is linked with the same line as before: gcc -fsanitize=address,undefined -o "build/host/bin/mruby_task_demo" .../mruby_task_demo.o "build/host/lib/libmruby.a" -L/usr/lib/x86_64-linux-gnu -lglib-2.0 -L/usr/lib/x86_64-linux-gnu -lgthread-2.0 -pthread -lglib-2.0 -lm Nothing in the standard gemboxes moves. Every core binary gem ships a tools/ and no src/ or mrblib/, so `@objs` is empty for all of them (:62, :96, :97) and the set `linker_attrs` returns is the one it returned before. The default build links bin/mruby and bin/mirb with byte-identical command lines, and `rake -m test` is green: $ rake -m test # exit 0 Total: 2089 OK: 2041 KO: 0 Crash: 0 Skip: 48 The example from mruby#5210 behaves as it did. A gem carrying only a tools/foo/main.c and `spec.linker.library_paths << "/usr/lib/foo"`, built beside mruby-bin-mirb, keeps that path off every line but its own, and it appears once in the whole run: $ MRUBY_CONFIG=issue5210.rb rake --verbose > log # exit 0 $ grep -c /usr/lib/foo log 1 $ grep -o 'gcc.*bin/foo".*' log gcc -L/usr/lib/foo -o "build/issue5210/bin/foo" .../main.o "build/issue5210/lib/libmruby.a" -lm $ grep -o 'gcc.*bin/mirb".*' log gcc -o "build/issue5210/bin/mirb" .../mirb.o ... "build/issue5210/lib/libmruby.a" -lm
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan includes up to 8 reviews per rolling hour; 0 remain after this review. 📝 WalkthroughWalkthrough
ChangesLinker attribute selection
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to The linker-option handling change is narrowly scoped and reported as verified without an identified merge-blocking issue; no actionable merge-blocking risk remains beyond normal checks and review. Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
MRuby::Gem::List#linker_attrsdrops every gem that builds a binary, and hands the rest to each link oflibmruby.a. 5c130e8 added that for #5210, where a gem's options reached binaries the gem had nothing to do with. It readsbin?as "not a library gem", and a gem can be both.build.libmruby_objs << @objs(lib/mruby/gem.rb:103) is unconditional, so a gem with asrc/or anmrblib/puts objects inlibmruby.awhatever else it builds. When such a gem also declaresspec.bins, its objects are in the archive and its linker options are not on the line that links it, so anything but its own binary fails to link.mruby-taskis that gem as soon asMRB_TASK_BUILD_DEMOis set: the define gives itspec.bins(mrbgems/mruby-task/mrbgem.rake:52), and it carriessrc/and aports/<port>/HAL whose glib port is what needs thesearch_packagelibraries.build_config/glib_hal_test.rbsets both, and mrbtest cannot be linked there:gcc -fsanitize=address,undefined -o "build/host/bin/mrbtest" .../driver.o .../vformat.o .../mrbtest.o .../mrbtest.a "build/host/lib/libmruby.a" -lmThe change
Drop a binary gem only when it contributes no object. A gem that put nothing in
libmruby.astill has options that belong to its own binary alone, which is what #5210 asked for; one that did needs them whereverlibmruby.ais linked.The second line of the diff follows from the first.
tasks/bin.rake:10asks forlinker_attrs(gem)when it links a gem's own binary, and a gem that now stays in the library set would be appended to it a second time, so its options would be doubled on that line. Append it only when it is not already there.Verified
build_config/glib_hal_test.rb, gcc 13.3.0, glib 2.80.0, from a clean tree. That config opens an anonymousMRuby::Buildon master, which is why the paths below saybuild/host. #7200 gives it a name and is stacked on this PR: with the two together itsrake testis green, bintest included.gcc -fsanitize=address,undefined -o "build/host/bin/mrbtest" .../driver.o .../vformat.o .../mrbtest.o .../mrbtest.a "build/host/lib/libmruby.a" -L/usr/lib/x86_64-linux-gnu -lglib-2.0 -L/usr/lib/x86_64-linux-gnu -lgthread-2.0 -pthread -lglib-2.0 -lmThose libraries appear twice because the gem calls
search_packagetwice, before and after this commit alike.build/host/bin/mruby_task_demois linked with the same line as before, which is what theinclude?guard is for:gcc -fsanitize=address,undefined -o "build/host/bin/mruby_task_demo" .../mruby_task_demo.o "build/host/lib/libmruby.a" -L/usr/lib/x86_64-linux-gnu -lglib-2.0 -L/usr/lib/x86_64-linux-gnu -lgthread-2.0 -pthread -lglib-2.0 -lmNothing in the standard gemboxes moves
Every core binary gem ships a
tools/and nosrc/ormrblib/, so@objsis empty for all of them (:62,:96,:97) and the setlinker_attrsreturns is the one it returned before. The default build linksbin/mrubyandbin/mirbwith byte-identical command lines, taken by removing them and rebuilding withrake --verboseon each side:#5210 behaves as it did
Its own example: a gem carrying only a
tools/foo/main.candspec.linker.library_paths << "/usr/lib/foo", built besidemruby-bin-mirb. The path stays off every line but its own, and appears once in the whole run:the gem and the config used
Environment
Details
The line each build gives
src/string.c, read offrake --verbosewith-MMD -c, the-Ilist and-odropped.glib_hal_testcallsenable_debug, which appends-g3 -O0after the gcc toolchain's own-g -O3; the other two do not: