Skip to content

build: build the Prism objects through the rules - #7248

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

build: build the Prism objects through the rules#7248
matz merged 1 commit into
mruby:masterfrom
takumin:prism-objects-through-rules

Conversation

@takumin

@takumin takumin commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

mruby-compiler compiles the Prism sources with a file task of its own
instead of the rules every other object of a gem is built by. A file task
has the prerequisites it is written with, here the source alone, so nothing
reads the .d a compile of a Prism object writes and nothing compares the
flags the object was compiled with against the flags of now. The 21 Prism
objects of a build (42 with the mrbc build) are the only objects outside
both, and they stay as they are through a change to a Prism header and
through a change to the compile flags.

The flags case is visible from the outside. PRISM_DEPTH_MAXIMUM is the
nesting cap Prism refuses input at, and mruby-compiler's mrbgem.rake sets it
to 256 unless the config sets it first. Setting it in the config of a build
directory that was built once leaves the cap where it was:

$ rake                                   # built once, cap 256
$ echo 'conf.cc.defines << "PRISM_DEPTH_MAXIMUM=64"' # added to the config
$ rake                                   # 165 CC, none of them Prism
$ bin/mruby -e "$(printf '[%.0s' {1..100})1$(printf ']%.0s' {1..100}); p 1"
1
$ grep -o 'PRISM_DEPTH_MAXIMUM=[0-9]*' build/host/mrbgems/mruby-compiler/lib/prism.o.flags
PRISM_DEPTH_MAXIMUM=256

Why

Dir.glob over the Prism sources registers each object with
file obj => [src] do prism_cc.run ... end. Rake takes the task as it is
written, so the object is up to date whenever it is newer than its .c;
the -MMD output beside it is written and never read, and
Compiler#get_dependencies, which reads the .d and discards an output
whose recorded flags differ (#7236), runs from the rules only. Everything
else a gem builds, its own sources and its generated gem_init.c, goes
through Compiler#define_rules.

The file task was where the compiler for the C++ ABI build was made: a
clone of the gem's cc with the C++ compile flag stripped and
MRC_ALLOC_LIBC added, cloned inside the action because Compiler#clone
is a deep copy and the mrbgem.rake body runs before the gem's version
define, the include paths of the gems it depends on and the presym include
path reach cc. A rule defined in the body would have had to name its
compiler in the body.

What this does

  • Compiler#define_rules takes a block that names the compiler to run
    with. The block is called when a rule is resolved, not when the rules
    are defined, so a compiler it derives from the receiver sees everything
    the build added to the receiver since. Without a block the rules run
    with the receiver, as before. get_dependencies becomes protected so
    the rules one compiler defines can ask the compiler they run.
  • mruby-compiler registers the Prism objects with define_rules and
    derives the C++ ABI compiler in the block, once. The objects mirror the
    Prism source tree (lib/util/pm_buffer.o for src/util/pm_buffer.c),
    since a rule maps an output to its source by path; the file task
    flattened them into lib/.

The compile lines do not change: every Prism object is byte-identical to
the one master builds at the same path, in the default and in the C++
ABI build, and so is bin/mrbc; bin/mruby differs only in the debug
line strings that spell the build directory.

Verified

rake -m -j16, CCACHE_DISABLE=1, gcc; the build directory built once on
master (0122b45) before each row, and the run after each row does
nothing in the "this PR" column.

change master this PR
touch lib/prism/include/prism/defines.h 70 CC: the compiler glue and the regenerated gem_init.c / mrblib.c, 0 of the 42 Prism objects 112 CC: the same 70 and the 42 Prism objects
conf.cc.defines << "PRISM_DEPTH_MAXIMUM=64" in the config 165 CC, 0 Prism; bin/mruby accepts the expression nested 100 deep, prism.o.flags says 256 207 CC, 42 Prism; bin/mruby refuses it with nesting too deep, prism.o.flags says 64
conf.cc.defines << "PROBE_FLAG" in the config, C++ ABI build (enable_cxx_abi) 165 CC, 0 Prism 207 CC, 42 Prism; the Prism record has -DMRC_ALLOC_LIBC and the presym include path and no -x c++, the glue record has -x c++
rake -m -j16 test from an empty build directory, default.rb with enable_bintest bintest OK 111, mrbtest OK 2073 bintest OK 111, mrbtest OK 2073, 0 KO, 0 crash
the same, C++ ABI build (default gembox, enable_cxx_abi) mrbtest OK 2073 mrbtest OK 2073, 0 KO, 0 crash

The 207 is 208 objects less build/host/mrbgems/gem_init.o, whose file
task in tasks/mrbgems.rake has no action and takes the rule's at execute
time, after Rake has decided it is up to date; that one is outside this PR.

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 lib/prism.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 -DPRISM_XALLOCATOR -DPRISM_DEPTH_MAXIMUM=256 -DMRC_TARGET_MRUBY -DPRISM_BUILD_MINIMAL -DMRBGEM_MRUBY_COMPILER_VERSION=0.0.0 -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"mrbgems/mruby-compiler/include" -I"mrbgems/mruby-compiler/lib/prism/include" -I"mrbgems/mruby-compiler/include" -I"build/host/include" -o "build/host/mrbgems/mruby-compiler/lib/prism.o" "mrbgems/mruby-compiler/lib/prism/src/prism.c"

The same object in the C++ ABI build, next to the glue that is built as
C++:

gcc -MMD -c -g -O3 -Wall -Wundef -Wwrite-strings -DPRISM_XALLOCATOR -DPRISM_DEPTH_MAXIMUM=256 -DMRC_TARGET_MRUBY -DPRISM_BUILD_MINIMAL -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -DMRBGEM_MRUBY_COMPILER_VERSION=0.0.0 -DMRC_ALLOC_LIBC -DMRB_USE_CXX_EXCEPTION -DMRB_USE_CXX_ABI ... -I"build/host-cxx/include" -o "build/host-cxx/mrbgems/mruby-compiler/lib/prism.o" "mrbgems/mruby-compiler/lib/prism/src/prism.c"
gcc -MMD -c -g -O3 -Wall -Wundef -Wwrite-strings -x c++ -std=gnu++03 -DPRISM_XALLOCATOR -DPRISM_DEPTH_MAXIMUM=256 -DMRC_TARGET_MRUBY -DPRISM_BUILD_MINIMAL -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -DMRBGEM_MRUBY_COMPILER_VERSION=0.0.0 -DMRB_USE_CXX_EXCEPTION -DMRB_USE_CXX_ABI ... -I"build/host-cxx/include" -o "build/host-cxx/mrbgems/mruby-compiler/src/ccontext.o" "mrbgems/mruby-compiler/src/ccontext.c"

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

Summary by CodeRabbit

  • Build Improvements
    • Improved compilation of Prism C sources with more consistent compiler settings.
    • Enhanced dependency tracking for Prism source files and headers.
    • Streamlined build rule handling to improve reliability across compiler configurations.

mruby-compiler compiled the Prism sources with a `file` task of its own
instead of the rules every other object of a gem is built by. A `file`
task has the prerequisites it is written with, here the source alone,
so nothing read the `.d` a compile of a Prism object writes and nothing
compared the flags the object was compiled with against the flags of
now. The 21 Prism objects of a build (42 with the `mrbc` build) were
the only objects outside both:

- A change to a Prism header rebuilt the compiler glue and not Prism.
  `touch lib/prism/include/prism/defines.h`, then `rake`: 70 objects
  recompiled, the glue and the regenerated `gem_init.c` / `mrblib.c`,
  and 0 of the 42 Prism objects.
- A change to the compile flags rebuilt every object but Prism.
  `conf.cc.defines << "PRISM_DEPTH_MAXIMUM=64"` in the config, then
  `rake`: 165 objects recompiled, 0 of the 42 Prism objects, and the
  `mruby` it linked still parsed an expression nested 100 deep, since
  the cap lives in `prism.c` and `prism.o` kept the cap it was built
  with. The record beside the object said `PRISM_DEPTH_MAXIMUM=256`.

Register the objects with `Compiler#define_rules`, as the objects of
every other gem are, so that they get the header prerequisites of their
`.d` and the flags comparison of every rule-built object. Both scenes
above now recompile the 42 Prism objects (112 and 207 objects), the
`rake` after them does nothing, and the nested expression is refused
with `nesting too deep`.

Under `MRB_USE_CXX_ABI` the Prism sources compile with a clone of the
gem's `cc`, the C++ compile flag stripped and `MRC_ALLOC_LIBC` added,
and the clone must be made once `cc` is complete: `Compiler#clone` is
a deep copy, and the mrbgem.rake body runs before the gem's version
define, the include paths of the gems it depends on and the presym
include path reach `cc`. The `file` task cloned inside its action for
that reason. `define_rules` now takes a block that names the compiler
when a rule is resolved, and mruby-compiler derives its compiler in
that block, once. Without a block the rules compile with the receiver
as before. `get_dependencies` becomes protected so the rules one
compiler defines can ask the compiler they run.

The objects mirror the Prism source tree, `lib/util/pm_buffer.o` for
`src/util/pm_buffer.c`, since a rule maps an output to its source by
path; the `file` task flattened them into `lib/`.
@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: b08e9990-2db7-494e-8e1e-f5ddc0ce84a7

📥 Commits

Reviewing files that changed from the base of the PR and between 0122b45 and c598dda.

📒 Files selected for processing (2)
  • lib/mruby/build/command.rb
  • mrbgems/mruby-compiler/mrbgem.rake

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


📝 Walkthrough

Walkthrough

Compiler#define_rules now supports lazy compiler selection for dependency resolution and rule execution. Prism C sources use this API with shared compiler settings, generated object paths, and conditional C++ ABI configuration.

Changes

Compiler rule selection

Layer / File(s) Summary
Lazy compiler selection and visibility
lib/mruby/build/command.rb
Compiler#define_rules accepts an optional compiler-selection block. Dependency resolution and rule execution use the selected compiler. Required compiler internals are protected during setup, then private visibility is restored.
Prism compilation integration
mrbgems/mruby-compiler/mrbgem.rake
Prism C sources use cc.define_rules. C++ ABI builds remove the C++ compile flag and add MRC_ALLOC_LIBC. Generated object paths support standard dependency tracking.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to c598d

This change makes Prism objects rebuild when relevant headers or compile flags change, with supplied default and C++ ABI checks passing; no actionable merge-blocking risk remains after normal review.

Sequence Diagram(s)

sequenceDiagram
  participant cc
  participant Compiler#define_rules
  participant Prism compiler
  cc->>Compiler#define_rules: Register Prism C sources
  Compiler#define_rules->>Prism compiler: Resolve dependencies and execute rules
  Prism compiler-->>Compiler#define_rules: Generate Prism object files
Loading

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 and concisely describes the main change: building Prism objects through compiler 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 90f5250 into mruby:master Aug 18, 2026
20 of 21 checks passed
@takumin
takumin deleted the prism-objects-through-rules branch August 18, 2026 01:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants