Skip to content

build: build the gem loader object through the rules - #7250

Merged
matz merged 1 commit into
mruby:masterfrom
takumin:gem-init-through-rules
Aug 18, 2026
Merged

build: build the gem loader object through the rules#7250
matz merged 1 commit into
mruby:masterfrom
takumin:gem-init-through-rules

Conversation

@takumin

@takumin takumin commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

build/<name>/mrbgems/gem_init.o, the object that boots every gem of a
build, is the one object tasks/mrbgems.rake declares by hand: a file
task without an action, with the generated gem_init.c and LEGAL as its
prerequisites. Rake finds the rule for it, but only when the task runs,
after it has already answered needed? from those two files alone. The
.d its compile writes and the flags record beside it are read by the
prerequisite procs of the rule, and for this object those never ran. A
header it includes changing, or a define reaching the build by a way that
leaves MRUBY_CONFIG untouched, left the old object in libmruby.a.

The header case is visible from the outside. gem_init.c reads mrb->c
and mrb->exc; add a field at the top of struct mrb_state and run
rake:

$ rake                        # built once
$ sed -i 's/^  struct mrb_jmpbuf \*jmp;$/&\n  void *probe_pad;/' include/mruby.h
$ rake                        # 152 CC, gem_init.o not among them
$ bin/mruby -e 'p 1'
Segmentation fault (core dumped)
$ rake                        # nothing to do
$ bin/mruby -e 'p 1'
Segmentation fault (core dumped)

Every other object of the build is compiled again; gem_init.o keeps the
old offset of mrb->c, and mrb_init_mrbgems dies at mrb->c->cibase
under mrb_open.

Why

file objfile(gem_init) => [gem_init.c, LEGAL] (2013, 15871eb) exists to
have a build write LEGAL without archiving it: LEGAL used to sit in
the object list of libmruby.a and was moved to a prerequisite of this
object. Rake's Task#execute runs enhance_with_matching_rule for a task
without an action, so the rule's action does compile the object, and its
prerequisite procs do run then; but that is after invoke decided from
the written prerequisites whether to execute at all. mrblib.o, the other
generated object of core, declares no task and is built by the rule from
Compiler#define_rules alone, which reads the .d and discards an output
whose recorded flags differ (#7236).

What this does

  • tasks/mrbgems.rake no longer declares the object. The rule pairs
    mrbgems/gem_init.o with mrbgems/gem_init.c in the build directory as
    it does for mrblib.o, and its .d and flags record take effect.
  • LEGAL becomes a product of the build in tasks/libmruby.rake, beside
    the libmruby.a whose contents it describes. It stays out of the
    archive as before, and it is still written by a plain rake. A build
    without a gem writes it too now, with the mruby license alone; the
    mrbc build (disable_libmruby) still does not.

Verified

rake -m -j16, CCACHE_DISABLE=1, gcc; default gembox with
enable_bintest and enable_test; the build directory built once on
master (0122b45) before each row, and the run after each row does
nothing in both columns.

change master this PR
a field added to struct mrb_state in include/mruby.h 152 CC, gem_init.o not among them; bin/mruby -e 'p 1' segfaults, and still does after another rake 153 CC, gem_init.o among them; bin/mruby -e 'p 1' prints 1
touch include/mruby.h 152 CC, gem_init.o not among them 153 CC
conf.cc.defines << "PROBE_FLAG=1" from an environment variable the config reads (MRUBY_CONFIG untouched) 165 CC, gem_init.o not among them, its flags record has no PROBE_FLAG 166 CC, gem_init.o among them, its flags record has -DPROBE_FLAG=1; the variable removed: 166 CC again
rake -m -j16 test from an empty build directory bintest OK 111, mrbtest OK 2073 bintest OK 111, mrbtest OK 2073, 0 KO, 0 crash
rake -m -j16 test MRUBY_CONFIG=ci/gcc-clang from an empty build directory every target 0 KO, 0 crash; LEGAL in every target's build directory
LEGAL after a clean build build/host/LEGAL, none in build/host/mrbc/, ar t libmruby.a has no LEGAL the same file, byte for byte, in the same places, and none in the archive
a config with no gem no LEGAL build/host/LEGAL with the mruby license alone

The 165 in the master column is 208 objects less the 42 Prism objects
(#7248) and this one.

Environment

Details
OS Linux 7.0.0-29-generic, x86_64
Compiler gcc 13.3.0
binutils 2.47
Ruby 4.0.6
rake 13.3.1

The compile line of gem_init.o in the default build, unchanged by this
PR (the record beside the object; %{flags} is the flags: line):

gcc -MMD -c -std=gnu99 -g -O3 -Wall -Wundef -Werror-implicit-function-declaration -Wwrite-strings -DHAVE_MRUBY_REGEXP_GEM -DMRB_USE_SET -DHAVE_MRUBY_IO_GEM -DMRB_USE_RATIONAL -DMRB_USE_COMPLEX -DMRB_USE_BIGINT -DMRB_USE_DEBUG_HOOK -I"include" -I"build/host/include" -o "build/host/mrbgems/gem_init.o" "build/host/mrbgems/gem_init.c"

No C source changes and the compile line is the same, so .text is
unaffected in every build.

Summary by CodeRabbit

  • Bug Fixes
    • Improved build packaging so the generated legal notices are included with static MRuby build products.
    • Prevented license text from being bundled inside the archive itself.
    • Simplified dependency handling for generated gem initialization files.

`build/<name>/mrbgems/gem_init.o` was the one object declared by hand:
an action-less `file` task whose prerequisites were the generated
`gem_init.c` and `LEGAL`. rake does find the rule for it, but only when
the task runs, after `needed?` was answered from those two files alone.
The `.d` the compile writes and the flags record are read by the
prerequisite procs of the rule, and for this object they never ran. A
header it includes changing, or a define that reaches the build without
touching `MRUBY_CONFIG`, left the old object in the archive.

Adding a field to `struct mrb_state` and running `rake` shows it: every
other object is compiled again, `gem_init.o` is not, and `bin/mruby`
reads `mrb->c` at the old offset and crashes at start. A second `rake`
changes nothing.

Drop the hand-written task so the object is built the way `mrblib.o` is:
by the rule that pairs it with `gem_init.c` and reads its `.d`. `LEGAL`
hung on that task only so that a build wrote it. It now hangs on the
products, beside the `libmruby.a` whose contents it describes, and stays
out of the archive as before. A build without a gem writes it too now,
with the mruby license alone.
@takumin
takumin requested a review from matz as a code owner August 18, 2026 01:47
@github-actions github-actions Bot added the build label Aug 18, 2026
@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: af8ff7b5-a471-408e-a891-78aa81a5e990

📥 Commits

Reviewing files that changed from the base of the PR and between 90f5250 and 950781a.

📒 Files selected for processing (2)
  • tasks/libmruby.rake
  • tasks/mrbgems.rake
💤 Files with no reviewable changes (1)
  • tasks/mrbgems.rake

Included review availability: Your plan includes up to 8 reviews per rolling hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

The build now registers the generated LEGAL file beside the static MRuby archive. The explicit gem_init dependency on LEGAL was removed.

Changes

MRuby build artifacts

Layer / File(s) Summary
License output registration
tasks/libmruby.rake, tasks/mrbgems.rake
The build products include #{build_dir}/LEGAL beside the archive. The explicit gem_init dependency on LEGAL was removed.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Merge Risk: ⚪ Minimal · up to 95078

This change makes the generated gem loader rebuild when its headers or recorded compiler flags change and moves LEGAL generation to the library build; no actionable merge-blocking risk remains after normal checks.

Suggested reviewers: matz

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: building the gem loader object through the build rules.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@matz
matz merged commit d50b504 into mruby:master Aug 18, 2026
20 of 21 checks passed
@takumin
takumin deleted the gem-init-through-rules branch August 18, 2026 01:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants