Skip to content

Commit dc2670c

Browse files
committed
ext(darwin): make sure symbols will be resolved correctly
See rake-compiler/rake-compiler-dock#87
1 parent 82c3d9e commit dc2670c

File tree

1 file changed

+36
-3
lines changed

1 file changed

+36
-3
lines changed

ext/nokogiri/extconf.rb

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,34 @@ def do_clean
584584
exit!(0)
585585
end
586586

587+
# In ruby 3.2, symbol resolution changed on Darwin, to introduce the `-bundle_loader` flag to
588+
# resolve symbols against the ruby binary.
589+
#
590+
# This makes it challenging to build a single extension that works with both a ruby with
591+
# `--enable-shared` and one with `--disable-shared. To work around that, we choose to add
592+
# `-flat_namespace` to the link line (later in this file).
593+
#
594+
# The `-flat_namespace` line introduces its own behavior change, which is that (similar to on
595+
# Linux), any symbols in the extension that are exported may now be resolved by shared libraries
596+
# loaded by the Ruby process. Specifically, that means that libxml2 and libxslt, which are
597+
# statically linked into the nokogiri bundle, will resolve (at runtime) to a system libxml2 loaded
598+
# by Ruby on Darwin. And it appears that often Ruby on Darwin does indeed load the system libxml2,
599+
# and that messes with our assumptions about whether we're running with a patched libxml2 or a
600+
# vanilla libxml2.
601+
#
602+
# We choose to use `-load_hidden` in this case to prevent exporting those symbols from libxml2 and
603+
# libxslt, which ensures that they will be resolved to the static libraries in the bundle. In other
604+
# words, when we use `load_hidden`, what happens in the extension stays in the extension.
605+
#
606+
# See https://github.com/rake-compiler/rake-compiler-dock/issues/87 for more info.
607+
#
608+
# Anyway, this method is the logical bit to tell us when to turn on these workarounds.
609+
def needs_darwin_linker_hack
610+
config_cross_build? &&
611+
darwin? &&
612+
Gem::Requirement.new("~> 3.2").satisfied_by?(Gem::Version.new(RbConfig::CONFIG["ruby_version"].split("+").first))
613+
end
614+
587615
#
588616
# main
589617
#
@@ -690,6 +718,10 @@ def do_clean
690718
cross_build_p = config_cross_build?
691719
message "Cross build is #{cross_build_p ? "enabled" : "disabled"}.\n"
692720

721+
if needs_darwin_linker_hack
722+
append_ldflags("-Wl,-flat_namespace")
723+
end
724+
693725
require "yaml"
694726
dependencies = YAML.load_file(File.join(PACKAGE_ROOT_DIR, "dependencies.yml"))
695727

@@ -941,16 +973,17 @@ def configure
941973
end.shelljoin
942974

943975
if static_p
976+
static_archive_ld_flag = needs_darwin_linker_hack ? ["-load_hidden"] : []
944977
$libs = $libs.shellsplit.map do |arg|
945978
case arg
946979
when "-lxml2"
947-
File.join(libxml2_recipe.path, "lib", libflag_to_filename(arg))
980+
static_archive_ld_flag + [File.join(libxml2_recipe.path, "lib", libflag_to_filename(arg))]
948981
when "-lxslt", "-lexslt"
949-
File.join(libxslt_recipe.path, "lib", libflag_to_filename(arg))
982+
static_archive_ld_flag + [File.join(libxslt_recipe.path, "lib", libflag_to_filename(arg))]
950983
else
951984
arg
952985
end
953-
end.shelljoin
986+
end.flatten.shelljoin
954987
end
955988

956989
ensure_func("xmlParseDoc", "libxml/parser.h")

0 commit comments

Comments
 (0)