Bug #22490
open`make -n build-ext` deletes `ext/extinit.c` and `ext/extinit.o` in a static-linked-ext build tree
Description
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=<list>.
Steps (via attached run-script)¶
- Configure with
--with-static-linked-extand some extensions, and run the
build to completion.ext/extinit.cexists, with oneinit(Init_…)line per
extension, and so doesext/extinit.o. - Run
sha256sum ruby ext/extinit.o ext/extinit.c. - Run
make -n build-ext. - Run
sha256sum ruby ext/extinit.o ext/extinit.cagain.
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 toextmk.rb, but that
is enough for make to treat the whole line as recursive. - So during
make -n build-ext,extmk.rbreally runs. In the-ntranscript
this shows asmake -f ext/configure-ext.mk -n …followed by oneextmk.rb
invocation per extension. ext/extmk.rb(line 702 in v3.3.3, 713 in v3.4.1, 715 onmaster) runs
FileUtils.rm_f(extinit.to_a)in the branch taken when$extlistis empty.
That deletesext/extinit.candext/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.rbwithout$(MAKE)appearing literally in
the recipe line, for example through an environment variable set elsewhere,
so that-napplies to it. - Or have
extmk.rbskip therm_fwhen it is invoked under a dry run, when
MAKEFLAGScontainsn. - Or, if this is intended, a note in the build documentation that
make -n build-extis not side-effect-free.
Cheers
Files