diff --git a/build_config/host-shared.rb b/build_config/host-shared.rb index 76e3a0713..9eaef55f2 100644 --- a/build_config/host-shared.rb +++ b/build_config/host-shared.rb @@ -1,36 +1,94 @@ -# NOTE: Currently, this configuration file does not support VisualC++! -# Your help is needed! +# 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.. +# libmruby.so.. symlink to libmruby.so (matches SONAME) +# libmruby.map linker version script (MRUBY_) +# +# 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| - # load specific toolchain settings conf.toolchain # include the GEM box conf.gembox 'default' - # C compiler settings + # -fPIC so the static archive's contents can be linked into the .so. conf.compilers.each do |cc| cc.flags << '-fPIC' end - conf.archiver do |archiver| - archiver.command = cc.command - archiver.archive_options = '-shared -o %{outfile} %{objs}' - end - - # file extensions - conf.exts do |exts| - exts.library = '.so' - end - - # file separator - # conf.file_separator = '/' - - # enable this if better compatibility with C++ is desired - #conf.enable_cxx_exception - # 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