From: "graial (al gray) via ruby-core" Date: 2026-11-02T01:09:05+00:00 Subject: [ruby-core:127290] [Ruby Bug#22490] `make -n build-ext` deletes `ext/extinit.c` and `ext/extinit.o` in a static-linked-ext build tree Issue #22490 has been reported by graial (al gray). ---------------------------------------- Bug #22490: `make -n build-ext` deletes `ext/extinit.c` and `ext/extinit.o` in a static-linked-ext build tree https://bugs.ruby-lang.org/issues/22490 * Author: graial (al gray) * Status: Open * ruby -v: 3.3.3 * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- This is a low priority, convenience issue, perhaps you have good reasons for this observed behaviour, but I am bringing it to your attention in any case. ## Summary In a completed build tree configured with `--with-static-linked-ext`, running the dry run `make -n build-ext` deletes `ext/extinit.c` and `ext/extinit.o`. A dry run is expected to change nothing. After it, the tree can no longer relink `ruby` correctly without first regenerating `ext/extinit.c` through `exts.mk`. ## Background This bug(?) was encountered while developing a custom ruby.wasm runtime for internet computer: https://gitlab.com/phrug/ruby_wasm_builder I first encountered this on ruby 3.3.3 as that's the latest ruby.wasm version capable of launching rails I've been developing a fork of ruby.wasm that adds a wasm32-unknown-icp target (proposed upstream as ruby/ruby.wasm#653) and publishes a release-bundle that the downstream builder can use to compile and link CRuby, then pack it into a wasm module. The prebuilt CRuby is libruby-static.a, ext/extinit.o and other objects the final link needs, together with the exact link command the build ran. With those, downstream projects like the builder can link their own ruby without rebuilding CRuby. That command from `make -n build-ext`, the only place I've found that prints the build���s own link line. In a tree with static extensions, running it deletes ext/extinit.o, which is one of the files I need to ship in the release bundle. Then, a later `make ruby` quietly produces a binary with no extensions. As a workaround, I run the dry run in a copy of the build tree. That works, but it costs a full copy of the tree for every build, plus the time required for the extra build. ## Environment - Ruby 4.0.9 (originally observed on 3.3.3). - GNU make. - Observed in a cross build for `wasm32-wasi`, using wasi-sdk 22.0, driven by ruby.wasm with `--with-static-linked-ext --with-ext=`. ## Steps (via attached run-script) 1. Configure with `--with-static-linked-ext` and some extensions, and run the build to completion. `ext/extinit.c` exists, with one `init(Init_���)` line per extension, and so does `ext/extinit.o`. 2. Run `sha256sum ruby ext/extinit.o ext/extinit.c`. 3. Run `make -n build-ext`. 4. Run `sha256sum ruby ext/extinit.o ext/extinit.c` again. **Expected:** the same three digests. **Actual:** `ruby` is unchanged, and `ext/extinit.o` and `ext/extinit.c` no longer exist. ## Where it comes from - `template/configure-ext.mk.tmpl` `$(Q)$(MINIRUBY) $(srcdir)/ext/extmk.rb --make='$(MAKE)' ���`. - GNU make runs any recipe line that references `$(MAKE)` even under `-n`, `-t` and `-q`. The GNU make manual documents this under "How the MAKE Variable Works". Here, `$(MAKE)` appears only as an *argument* to `extmk.rb`, but that is enough for make to treat the whole line as recursive. - So during `make -n build-ext`, `extmk.rb` really runs. In the `-n` transcript this shows as `make -f ext/configure-ext.mk -n ���` followed by one `extmk.rb` invocation per extension. - `ext/extmk.rb` (line 702 in v3.3.3, 713 in v3.4.1, 715 on `master`) runs `FileUtils.rm_f(extinit.to_a)` in the branch taken when `$extlist` is empty. That deletes `ext/extinit.c` and `ext/extinit.o`. ## Why it matters `make -n` is a natural way to find out what link a build would perform. Here it modifies the tree it inspects, and nothing says so. A follow-up `make ruby` against the top-level `Makefile` then regenerates `ext/extinit.c` from an empty `$(EXTINITS)`, because `EXTINITS` is defined only in `exts.mk`. It writes an `Init_ext` that names no extensions and relinks `ruby` without them, and it reports success. ## Possible directions These are for the maintainers to judge. I may be missing why the line is written this way. - Pass the make command to `extmk.rb` without `$(MAKE)` appearing literally in the recipe line, for example through an environment variable set elsewhere, so that `-n` applies to it. - Or have `extmk.rb` skip the `rm_f` when it is invoked under a dry run, when `MAKEFLAGS` contains `n`. - Or, if this is intended, a note in the build documentation that `make -n build-ext` is not side-effect-free. Cheers ---Files-------------------------------- cruby-make-n-build-ext-runsheet.sh (4.2 KB) -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/