Files
Yukihiro "Matz" Matsumoto c0fb9a190c host-shared.rb: produce libmruby.so for distro packaging
Builds the shared library FROM the existing static archive via
-Wl,--whole-archive, leaving the static-build pipeline (and the
test infrastructure that depends on it) untouched.

Produces in build/host/lib/:
  libmruby.so              SONAME=libmruby.so.<MAJOR>.<MINOR>
  libmruby.so.<MAJOR>.<MINOR>  symlink for runtime DT_NEEDED resolution
  libmruby.map             linker version script: MRUBY_<RELEASE_NO>
  libmruby_core.so + symlink (similarly)

Symbol versioning ties to MRUBY_RELEASE_NO (e.g. MRUBY_40000 for
4.0.0). mruby has historically had ABI breaks at TEENY granularity,
so the version tag uses the full release number rather than just
MAJOR.MINOR.

Closes #6239.

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-08 20:48:58 +09:00

95 lines
3.1 KiB
Ruby

# Build mruby with a shared libmruby.so (in addition to the usual
# libmruby.a / executables).
#
# Produces (in build/host/lib/):
# libmruby.a the static archive (as in the default build)
# libmruby.so the shared library, with SONAME=libmruby.so.<MAJOR>.<MINOR>
# libmruby.so.<MAJOR>.<MINOR> symlink to libmruby.so (matches SONAME)
# libmruby.map linker version script (MRUBY_<RELEASE_NO>)
#
# Also produces the matching libmruby_core.so + symlink for completeness.
#
# Symbol versioning ties to MRUBY_RELEASE_NO (e.g. MRUBY_40000 for 4.0.0).
# mruby has historically had ABI breaks between TEENY versions, so the
# version tag uses the full release number rather than just MAJOR.MINOR.
#
# The shared library is built FROM the static archive via
# `-Wl,--whole-archive`, so the existing static-build pipeline (including
# the test infrastructure) is unaffected. Executables in build/host/bin
# remain statically linked; distros that want dynamically-linked
# executables can rebuild them against the .so.
#
# NOTE: gcc/clang only — VisualC++ support requires a separate config.
require "mruby/source"
MRuby::Build.new do |conf|
conf.toolchain
# include the GEM box
conf.gembox 'default'
# -fPIC so the static archive's contents can be linked into the .so.
conf.compilers.each do |cc|
cc.flags << '-fPIC'
end
# Turn on `enable_debug` for better debugging
conf.enable_debug
conf.enable_bintest
conf.enable_test
end
# Add the shared-library targets as a post-build pass, so the default
# static-build pipeline remains untouched.
MRuby.each_target do
next unless name == "host"
libdir = File.join(build_dir, libdir_name)
vermap = File.join(build_dir, "libmruby.map")
vertag = "MRUBY_#{MRuby::Source::MRUBY_RELEASE_NO}"
# Generate the version script eagerly — it has no .o dependencies and is
# tiny enough that lazy generation isn't worth the rake plumbing.
mkdir_p File.dirname(vermap)
File.write(vermap, <<~MAP)
#{vertag} {
global: *;
local: *;
};
MAP
major = MRuby::Source::MRUBY_RELEASE_MAJOR
minor = MRuby::Source::MRUBY_RELEASE_MINOR
[
[libmruby_static, "libmruby"],
[libmruby_core_static, "libmruby_core"],
].each do |archive, basename|
so = File.join(libdir, "#{basename}.so")
symlink = "#{so}.#{major}.#{minor}"
soname = "#{basename}.so.#{major}.#{minor}"
# Build .so from the static archive via --whole-archive.
file so => [archive, vermap] do |t|
_pp "LD", so.relative_path
sh "#{cc.command} -shared -fPIC -o #{so}" \
" -Wl,-soname,#{soname}" \
" -Wl,--version-script=#{vermap}" \
" -Wl,--whole-archive #{archive} -Wl,--no-whole-archive" \
" -lm"
end
products << so
# SONAME-matching symlink: needed so executables linked with -lmruby
# (which embeds the SONAME as DT_NEEDED) can find the library at
# runtime via standard search paths.
file symlink => so do |t|
_pp "LN", "#{symlink.relative_path} -> #{File.basename(so)}"
rm_f symlink
File.symlink(File.basename(so), symlink)
end
products << symlink
end
end