From 8e5fa3313277e08c15e4c1bb74b31b5c80ba311c Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Sun, 6 Sep 2020 15:39:06 -0400 Subject: [PATCH 01/11] gem: get ready for darwin where the gem platform name is different from RUBY_PLATFORM. - call the gem task correctly - have `nokogiri -v` display Gem::Platform.local --- lib/nokogiri/version.rb | 1 + tasks/cross-ruby.rb | 2 +- test/test_version.rb | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/nokogiri/version.rb b/lib/nokogiri/version.rb index 770cc2ff7f4..91c414e992e 100644 --- a/lib/nokogiri/version.rb +++ b/lib/nokogiri/version.rb @@ -69,6 +69,7 @@ def to_hash vi["ruby"] = {}.tap do |ruby| ruby["version"] = ::RUBY_VERSION ruby["platform"] = ::RUBY_PLATFORM + ruby["gem_platform"] = ::Gem::Platform.local.to_s ruby["description"] = ::RUBY_DESCRIPTION ruby["engine"] = engine ruby["jruby"] = jruby? if jruby? diff --git a/tasks/cross-ruby.rb b/tasks/cross-ruby.rb index 35c098f47ea..e05e571857e 100644 --- a/tasks/cross-ruby.rb +++ b/tasks/cross-ruby.rb @@ -179,7 +179,7 @@ def verify_dll(dll, cross_ruby) task "guest" do # use Task#invoke because the pkg/*gem task is defined at runtime Rake::Task["native:#{plat}"].invoke - Rake::Task["pkg/#{HOE.spec.full_name}-#{plat}.gem"].invoke + Rake::Task["pkg/#{HOE.spec.full_name}-#{Gem::Platform.new(plat).to_s}.gem"].invoke end end end diff --git a/test/test_version.rb b/test/test_version.rb index 3fdeaca586a..21d3fa3f089 100644 --- a/test/test_version.rb +++ b/test/test_version.rb @@ -14,6 +14,7 @@ def test_version_info_basics assert_equal Nokogiri::VERSION_INFO["ruby"]["version"], ::RUBY_VERSION assert_equal Nokogiri::VERSION_INFO["ruby"]["platform"], ::RUBY_PLATFORM + assert_equal Nokogiri::VERSION_INFO["ruby"]["gem_platform"], ::Gem::Platform.local.to_s end def test_version_info_for_xerces From 08f6a4cb34c084155082157bfeb64effafc2b351 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Sun, 6 Sep 2020 15:43:08 -0400 Subject: [PATCH 02/11] gem: move RakeExtension tasks from Rakefile into tasks/cross-ruby.rb so all our cross-compilation and native gem logic is in one place --- Rakefile | 63 +-------------------------------------------- tasks/cross-ruby.rb | 60 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 62 deletions(-) diff --git a/Rakefile b/Rakefile index f5f6f477351..cf2d19ec2ba 100644 --- a/Rakefile +++ b/Rakefile @@ -10,7 +10,6 @@ Hoe.plugin :markdown require 'shellwords' require_relative "tasks/util" -require_relative "tasks/cross-ruby" HOE = Hoe.spec 'nokogiri' do developer 'Aaron Patterson', 'aaronp@rubyforge.org' @@ -74,67 +73,7 @@ HOE = Hoe.spec 'nokogiri' do self.test_prelude = 'require "helper"' # ensure simplecov gets loaded before anything else end -if java? - # TODO: clean this section up. - require "rake/javaextensiontask" - Rake::JavaExtensionTask.new("nokogiri", HOE.spec) do |ext| - jruby_home = RbConfig::CONFIG['prefix'] - ext.ext_dir = 'ext/java' - ext.lib_dir = 'lib/nokogiri' - ext.source_version = '1.6' - ext.target_version = '1.6' - jars = ["#{jruby_home}/lib/jruby.jar"] + FileList['lib/*.jar'] - ext.classpath = jars.map { |x| File.expand_path x }.join ':' - ext.debug = true if ENV['JAVA_DEBUG'] - end - - task gem_build_path => [:compile] do - add_file_to_gem 'lib/nokogiri/nokogiri.jar' - end -else - require "rake/extensiontask" - - HOE.spec.files.reject! { |f| f =~ %r{\.(java|jar)$} } - - dependencies = YAML.load_file("dependencies.yml") - - task gem_build_path do - %w[libxml2 libxslt].each do |lib| - version = dependencies[lib]["version"] - archive = File.join("ports", "archives", "#{lib}-#{version}.tar.gz") - add_file_to_gem archive - patchesdir = File.join("patches", lib) - patches = `#{['git', 'ls-files', patchesdir].shelljoin}`.split("\n").grep(/\.patch\z/) - patches.each { |patch| - add_file_to_gem patch - } - (untracked = Dir[File.join(patchesdir, '*.patch')] - patches).empty? or - at_exit { - untracked.each { |patch| - puts "** WARNING: untracked patch file not added to gem: #{patch}" - } - } - end - end - - Rake::ExtensionTask.new("nokogiri", HOE.spec) do |ext| - ext.lib_dir = File.join(*['lib', 'nokogiri', ENV['FAT_DIR']].compact) - ext.config_options << ENV['EXTOPTS'] - ext.cross_compile = true - ext.cross_platform = CROSS_RUBIES.map(&:platform).uniq - ext.cross_config_options << "--enable-cross-build" - ext.cross_compiling do |spec| - libs = dependencies.map { |name, dep| "#{name}-#{dep["version"]}" }.join(', ') - - spec.post_install_message = <<-EOS -Nokogiri is built with the packaged libraries: #{libs}. - EOS - spec.files.reject! { |path| File.fnmatch?('ports/*', path) } - spec.dependencies.reject! { |dep| dep.name=='mini_portile2' } - end - end -end - +require_relative "tasks/cross-ruby" require_relative "tasks/concourse" require_relative "tasks/css-generate" require_relative "tasks/debug" diff --git a/tasks/cross-ruby.rb b/tasks/cross-ruby.rb index e05e571857e..d1940e1ee24 100644 --- a/tasks/cross-ruby.rb +++ b/tasks/cross-ruby.rb @@ -195,3 +195,63 @@ def verify_dll(dll, cross_ruby) RakeCompilerDock.sh "gem install bundler --no-document && bundle && rake java gem", rubyvm: "jruby" end end + +if java? + require "rake/javaextensiontask" + Rake::JavaExtensionTask.new("nokogiri", HOE.spec) do |ext| + jruby_home = RbConfig::CONFIG['prefix'] + ext.ext_dir = 'ext/java' + ext.lib_dir = 'lib/nokogiri' + ext.source_version = '1.6' + ext.target_version = '1.6' + jars = ["#{jruby_home}/lib/jruby.jar"] + FileList['lib/*.jar'] + ext.classpath = jars.map { |x| File.expand_path x }.join ':' + ext.debug = true if ENV['JAVA_DEBUG'] + end + + task gem_build_path => [:compile] do + add_file_to_gem 'lib/nokogiri/nokogiri.jar' + end +else + require "rake/extensiontask" + + HOE.spec.files.reject! { |f| f =~ %r{\.(java|jar)$} } + + dependencies = YAML.load_file("dependencies.yml") + + task gem_build_path do + %w[libxml2 libxslt].each do |lib| + version = dependencies[lib]["version"] + archive = File.join("ports", "archives", "#{lib}-#{version}.tar.gz") + add_file_to_gem archive + patchesdir = File.join("patches", lib) + patches = `#{['git', 'ls-files', patchesdir].shelljoin}`.split("\n").grep(/\.patch\z/) + patches.each { |patch| + add_file_to_gem patch + } + (untracked = Dir[File.join(patchesdir, '*.patch')] - patches).empty? or + at_exit { + untracked.each { |patch| + puts "** WARNING: untracked patch file not added to gem: #{patch}" + } + } + end + end + + Rake::ExtensionTask.new("nokogiri", HOE.spec) do |ext| + ext.lib_dir = File.join(*['lib', 'nokogiri', ENV['FAT_DIR']].compact) + ext.config_options << ENV['EXTOPTS'] + ext.cross_compile = true + ext.cross_platform = CROSS_RUBIES.map(&:platform).uniq + ext.cross_config_options << "--enable-cross-build" + ext.cross_compiling do |spec| + libs = dependencies.map { |name, dep| "#{name}-#{dep["version"]}" }.join(', ') + + spec.post_install_message = <<-EOS +Nokogiri is built with the packaged libraries: #{libs}. + EOS + spec.files.reject! { |path| File.fnmatch?('ports/*', path) } + spec.dependencies.reject! { |dep| dep.name=='mini_portile2' } + end + end +end From 69ec3ec0f6d9a53417dd4a9f0143d74acd12c656 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Sun, 6 Sep 2020 16:01:30 -0400 Subject: [PATCH 03/11] gem: add darwin gems to .cross_rubies - update CrossRuby to recognize it - improve the parsing code for .cross_rubies - only create rake-compiler-docker rake tasks for windows and linux --- .cross_rubies | 6 +++++- tasks/cross-ruby.rb | 19 ++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.cross_rubies b/.cross_rubies index 57c8fb0ff57..eecbd327e0e 100644 --- a/.cross_rubies +++ b/.cross_rubies @@ -2,15 +2,19 @@ 2.7.0:x86_64-w64-mingw32 2.7.0:i686-linux-gnu 2.7.0:x86_64-linux-gnu +2.7.0:x86_64-darwin19 2.6.0:i686-w64-mingw32 2.6.0:x86_64-w64-mingw32 2.6.0:i686-linux-gnu 2.6.0:x86_64-linux-gnu +2.6.0:x86_64-darwin19 2.5.0:i686-w64-mingw32 2.5.0:x86_64-w64-mingw32 2.5.0:i686-linux-gnu 2.5.0:x86_64-linux-gnu +2.5.0:x86_64-darwin19 2.4.0:i686-w64-mingw32 2.4.0:x86_64-w64-mingw32 2.4.0:i686-linux-gnu -2.4.0:x86_64-linux-gnu \ No newline at end of file +2.4.0:x86_64-linux-gnu +2.4.0:x86_64-darwin19 diff --git a/tasks/cross-ruby.rb b/tasks/cross-ruby.rb index d1940e1ee24..1f3d01a677e 100644 --- a/tasks/cross-ruby.rb +++ b/tasks/cross-ruby.rb @@ -26,6 +26,8 @@ def platform "x86_64-linux" when /\Ai[3-6]86.*linux/ "x86-linux" + when /\Ax86_64-darwin19/ + "x86_64-darwin19" else raise "unsupported host: #{host}" end @@ -103,14 +105,12 @@ def dll_ref_versions end end -CROSS_RUBIES = File.read(".cross_rubies").lines.flat_map do |line| +CROSS_RUBIES = File.read(".cross_rubies").split("\n").map do |line| case line when /\A([^#]+):([^#]+)/ CrossRuby.new($1, $2) - else - [] end -end +end.compact ENV["RUBY_CC_VERSION"] = CROSS_RUBIES.map(&:ver).uniq.join(":") @@ -159,11 +159,8 @@ def verify_dll(dll, cross_ruby) end namespace "gem" do - CROSS_RUBIES.map(&:platform).uniq.each do |plat| - desc "build native fat binary gems for windows and linux" - multitask "native" => plat - - desc "build native gem for #{plat} platform\nthis trampolines into the rake-compiler-dock guest" + CROSS_RUBIES.find_all { |cr| cr.windows? || cr.linux? }.map(&:platform).uniq.each do |plat| + desc "build native gem for #{plat} platform (host)" task plat do # TODO remove `find` after https://github.com/rake-compiler/rake-compiler/pull/171 is shipped RakeCompilerDock.sh <<-EOT, platform: plat @@ -175,7 +172,7 @@ def verify_dll(dll, cross_ruby) end namespace plat do - desc "(within docker guest) build native gem for #{plat} platform\nthis should only be called within a rake-compiler-dock image" + desc "build native gem for #{plat} platform (guest)" task "guest" do # use Task#invoke because the pkg/*gem task is defined at runtime Rake::Task["native:#{plat}"].invoke @@ -190,7 +187,7 @@ def verify_dll(dll, cross_ruby) desc "build native fat binary gems for linux" multitask "linux" => CROSS_RUBIES.map(&:platform).uniq.grep(LINUX_PLATFORM_REGEX) - desc "build a jruby gem with docker" + desc "build a jruby gem" task "jruby" do RakeCompilerDock.sh "gem install bundler --no-document && bundle && rake java gem", rubyvm: "jruby" end From 2202c479684d4346b7d03af629acb80693c8b1e8 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Sun, 6 Sep 2020 16:51:16 -0400 Subject: [PATCH 04/11] gem: placeholder for darwin .bundle checks --- tasks/cross-ruby.rb | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tasks/cross-ruby.rb b/tasks/cross-ruby.rb index 1f3d01a677e..f1ce6a2444b 100644 --- a/tasks/cross-ruby.rb +++ b/tasks/cross-ruby.rb @@ -36,11 +36,20 @@ def platform WINDOWS_PLATFORM_REGEX = /mingw|mswin/ MINGW32_PLATFORM_REGEX = /mingw32/ LINUX_PLATFORM_REGEX = /linux/ + DARWIN_PLATFORM_REGEX = /darwin/ def windows? !!(platform =~ WINDOWS_PLATFORM_REGEX) end + def linux? + !!(platform =~ LINUX_PLATFORM_REGEX) + end + + def darwin? + !!(platform =~ DARWIN_PLATFORM_REGEX) + end + def tool(name) (@binutils_prefix ||= case platform when "x64-mingw32" @@ -51,6 +60,8 @@ def tool(name) "x86_64-linux-gnu-" when "x86-linux" "i686-linux-gnu-" + else + "" end) + name end @@ -119,6 +130,7 @@ def dll_ref_versions def verify_dll(dll, cross_ruby) dll_imports = cross_ruby.dlls dump = `#{["env", "LANG=C", cross_ruby.tool("objdump"), "-p", dll].shelljoin}` + if cross_ruby.windows? raise "unexpected file format for generated dll #{dll}" unless /file format #{Regexp.quote(cross_ruby.target)}\s/ === dump raise "export function Init_nokogiri not in dll #{dll}" unless /Table.*\sInit_nokogiri\s/mi === dump @@ -129,7 +141,8 @@ def verify_dll(dll, cross_ruby) if dll_imports_is.sort != dll_imports.sort raise "unexpected dll imports #{dll_imports_is.inspect} in #{dll}" end - else + + elsif cross_ruby.linux? # Verify that the expected so dependencies match the actual dependencies # and that no further dependencies exist. dll_imports_is = dump.scan(/NEEDED\s+(.*)/).map(&:first).uniq @@ -148,12 +161,21 @@ def verify_dll(dll, cross_ruby) if dll_ref_versions_is != cross_ruby.dll_ref_versions raise "unexpected so version requirements #{dll_ref_versions_is.inspect} in #{dll}" end + + elsif cross_ruby.darwin? + raise "need to implement .bundle checks" + end puts "verify_dll: #{dll}: passed shared library sanity checks" end CROSS_RUBIES.each do |cross_ruby| - task "tmp/#{cross_ruby.platform}/stage/lib/nokogiri/#{cross_ruby.minor_ver}/nokogiri.so" do |t| + staging_path_dll = if cross_ruby.darwin? + "tmp/#{cross_ruby.platform}/stage/lib/nokogiri/#{cross_ruby.minor_ver}/nokogiri.bundle" + else + "tmp/#{cross_ruby.platform}/stage/lib/nokogiri/#{cross_ruby.minor_ver}/nokogiri.so" + end + task staging_path_dll do |t| verify_dll t.name, cross_ruby end end From ac1c1fe426d80008aa773731121df91609329501 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 7 Sep 2020 07:44:17 -0400 Subject: [PATCH 05/11] dev: move build_all into scripts/ --- .hoerc | 3 ++- build_all => scripts/build-gems | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename build_all => scripts/build-gems (100%) diff --git a/.hoerc b/.hoerc index 76589f5644b..e4aaa586b29 100644 --- a/.hoerc +++ b/.hoerc @@ -26,15 +26,16 @@ exclude: !ruby/regexp '/ |Manifest.txt |Rakefile |appveyor\.yml - |build_all |CHANGELOG.md |CODE_OF_CONDUCT.md |CONTRIBUTING.md |ROADMAP.md |SECURITY.md |STANDARD_RESPONSES.md + |Vagrantfile |Y_U_NO_GEMSPEC.md |C_CODING_STYLE.* + |scripts |patches ) |\.gitkeep diff --git a/build_all b/scripts/build-gems similarity index 100% rename from build_all rename to scripts/build-gems From 79ff218170206c8f8f99c70433860250755541ad Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 7 Sep 2020 07:44:54 -0400 Subject: [PATCH 06/11] gem: setup-osx-native-builders to set up rubies for native gem --- scripts/setup-osx-native-builders | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100755 scripts/setup-osx-native-builders diff --git a/scripts/setup-osx-native-builders b/scripts/setup-osx-native-builders new file mode 100755 index 00000000000..22f3222ecb9 --- /dev/null +++ b/scripts/setup-osx-native-builders @@ -0,0 +1,41 @@ +#! /usr/bin/env bash + +# https://github.com/postmodern/chruby +# (your file locations may be different) +source /opt/dev/sh/chruby/chruby.sh +RUBIES_DIR=${HOME}/.rubies + +# prerequisites on OSX +if [[ -z "$(which ruby-install)" ]] ; then + echo "ERROR: ruby-install is not installed, please install it: https://github.com/postmodern/ruby-install" + exit 1 +fi + +set -o errexit +set -o pipefail + +CROSS_FILE=".cross_rubies" +RUBIES=$(cat $CROSS_FILE | fgrep darwin | cut -d. -f1,2 | sort -u) +RAKE_COMPILER_CONFIG_DIR=${HOME}/.rake-compiler +RAKE_COMPILER_CONFIG=${RAKE_COMPILER_CONFIG_DIR}/config.yml + +mkdir -p $RAKE_COMPILER_CONFIG_DIR +echo "---" > $RAKE_COMPILER_CONFIG + +for ruby in $RUBIES ; do + ruby_zero="${ruby}.0" + ruby_fullname="native-builder-${ruby}" + if ! chruby $ruby_fullname ; then + echo "installing $ruby_fullname ..." + ruby-install -i ${RUBIES_DIR}/${ruby_fullname} ruby ${ruby} -- --disable-shared + source /opt/dev/sh/chruby/chruby.sh + else + echo "ruby $ruby_fullname is installed" + fi + + chruby $ruby_fullname + platform=$(ruby -e 'puts RUBY_PLATFORM') + echo "rbconfig-${platform}-${ruby_zero}: \"${RUBIES_DIR}/${ruby_fullname}/lib/ruby/${ruby_zero}/${platform}/rbconfig.rb\"" >> $RAKE_COMPILER_CONFIG +done + +cat $RAKE_COMPILER_CONFIG From fc72c85053832fadd0714e725872f4f6daf0f79e Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 7 Sep 2020 08:02:15 -0400 Subject: [PATCH 07/11] gem: rake tasks for native darwin gem and make sure we gitignore the .bundle files --- .gitignore | 3 +-- tasks/cross-ruby.rb | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 2c4db5c5ac3..d8ece1227e0 100644 --- a/.gitignore +++ b/.gitignore @@ -24,9 +24,8 @@ ext/java/nokogiri/**/*.class ext/nokogiri/*.dll gems lib/nokogiri/**/nokogiri.so -lib/nokogiri/nokogiri.bundle +lib/nokogiri/**/nokogiri.bundle lib/nokogiri/nokogiri.jar -lib/nokogiri/nokogiri.rb pkg ports stash diff --git a/tasks/cross-ruby.rb b/tasks/cross-ruby.rb index f1ce6a2444b..c3fd89f5523 100644 --- a/tasks/cross-ruby.rb +++ b/tasks/cross-ruby.rb @@ -203,16 +203,28 @@ def verify_dll(dll, cross_ruby) end end - desc "build native fat binary gems for windows" - multitask "windows" => CROSS_RUBIES.map(&:platform).uniq.grep(WINDOWS_PLATFORM_REGEX) - - desc "build native fat binary gems for linux" - multitask "linux" => CROSS_RUBIES.map(&:platform).uniq.grep(LINUX_PLATFORM_REGEX) - desc "build a jruby gem" task "jruby" do RakeCompilerDock.sh "gem install bundler --no-document && bundle && rake java gem", rubyvm: "jruby" end + + CROSS_RUBIES.find_all { |cr| cr.darwin? }.map(&:platform).uniq.each do |plat| + desc "build native gem for #{plat} platform" + task plat do + sh "find ~/.gem -name extensiontask.rb | while read f ; do sed -i '' 's/callback.call(spec) if callback/@cross_compiling.call(spec) if @cross_compiling/' \$f ; done" + Rake::Task["native:#{plat}"].invoke + Rake::Task["pkg/#{HOE.spec.full_name}-#{Gem::Platform.new(plat).to_s}.gem"].invoke + end + end + + desc "build native gems for windows" + multitask "windows" => CROSS_RUBIES.find_all(&:windows?).map(&:platform).uniq + + desc "build native gems for linux" + multitask "linux" => CROSS_RUBIES.find_all(&:linux?).map(&:platform).uniq + + desc "build native gems for darwin" + multitask "darwin" => CROSS_RUBIES.find_all(&:darwin?).map(&:platform).uniq end if java? From 34efda88f5c7dab9ed97403e0cca13352cde4c46 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 7 Sep 2020 09:42:21 -0400 Subject: [PATCH 08/11] dev: clean up verify_dll tests of native gem shared libraries --- tasks/cross-ruby.rb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tasks/cross-ruby.rb b/tasks/cross-ruby.rb index c3fd89f5523..462ca1f7b15 100644 --- a/tasks/cross-ruby.rb +++ b/tasks/cross-ruby.rb @@ -128,7 +128,7 @@ def dll_ref_versions require "rake_compiler_dock" def verify_dll(dll, cross_ruby) - dll_imports = cross_ruby.dlls + expected_imports = cross_ruby.dlls.sort dump = `#{["env", "LANG=C", cross_ruby.tool("objdump"), "-p", dll].shelljoin}` if cross_ruby.windows? @@ -137,29 +137,29 @@ def verify_dll(dll, cross_ruby) # Verify that the expected DLL dependencies match the actual dependencies # and that no further dependencies exist. - dll_imports_is = dump.scan(/DLL Name: (.*)$/).map(&:first).map(&:downcase).uniq - if dll_imports_is.sort != dll_imports.sort - raise "unexpected dll imports #{dll_imports_is.inspect} in #{dll}" + actual_imports = dump.scan(/DLL Name: (.*)$/).map(&:first).map(&:downcase).uniq.sort + if actual_imports != expected_imports + raise "unexpected so imports #{actual_imports.inspect} in #{dll} (expected #{expected_imports.inspect})" end elsif cross_ruby.linux? # Verify that the expected so dependencies match the actual dependencies # and that no further dependencies exist. - dll_imports_is = dump.scan(/NEEDED\s+(.*)/).map(&:first).uniq - if dll_imports_is.sort != dll_imports.sort - raise "unexpected so imports #{dll_imports_is.inspect} in #{dll} (expected #{dll_imports.inspect})" + actual_imports = dump.scan(/NEEDED\s+(.*)/).map(&:first).uniq.sort + if actual_imports != expected_imports + raise "unexpected so imports #{actual_imports.inspect} in #{dll} (expected #{expected_imports.inspect})" end # Verify that the expected so version requirements match the actual dependencies. - dll_ref_versions_list = dump.scan(/0x[\da-f]+ 0x[\da-f]+ \d+ (\w+)_([\d\.]+)$/i) + ref_versions_data = dump.scan(/0x[\da-f]+ 0x[\da-f]+ \d+ (\w+)_([\d\.]+)$/i) # Build a hash of library versions like {"LIBUDEV"=>"183", "GLIBC"=>"2.17"} - dll_ref_versions_is = dll_ref_versions_list.each.with_object({}) do |(lib, ver), h| + actual_ref_versions = ref_versions_data.each.with_object({}) do |(lib, ver), h| if !h[lib] || ver.split(".").map(&:to_i).pack("C*") > h[lib].split(".").map(&:to_i).pack("C*") h[lib] = ver end end - if dll_ref_versions_is != cross_ruby.dll_ref_versions - raise "unexpected so version requirements #{dll_ref_versions_is.inspect} in #{dll}" + if actual_ref_versions != cross_ruby.dll_ref_versions + raise "unexpected so version requirements #{actual_ref_versions.inspect} in #{dll}" end elsif cross_ruby.darwin? From dd9705cbb6c44ec8945e4f5363e2bc0d16a40a1c Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 7 Sep 2020 09:43:21 -0400 Subject: [PATCH 09/11] dev: move dll staging path logic into CrossRuby --- tasks/cross-ruby.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tasks/cross-ruby.rb b/tasks/cross-ruby.rb index 462ca1f7b15..411053461be 100644 --- a/tasks/cross-ruby.rb +++ b/tasks/cross-ruby.rb @@ -74,6 +74,14 @@ def target end end + def dll_ext + darwin? ? "bundle" : "so" + end + + def dll_staging_path + "tmp/#{platform}/stage/lib/#{HOE.spec.name}/#{minor_ver}/#{HOE.spec.name}.#{dll_ext}" + end + def libruby_dll case platform when "x64-mingw32" @@ -170,12 +178,7 @@ def verify_dll(dll, cross_ruby) end CROSS_RUBIES.each do |cross_ruby| - staging_path_dll = if cross_ruby.darwin? - "tmp/#{cross_ruby.platform}/stage/lib/nokogiri/#{cross_ruby.minor_ver}/nokogiri.bundle" - else - "tmp/#{cross_ruby.platform}/stage/lib/nokogiri/#{cross_ruby.minor_ver}/nokogiri.so" - end - task staging_path_dll do |t| + task cross_ruby.dll_staging_path do |t| verify_dll t.name, cross_ruby end end From d88c368cf512d0e646f07828493837a1236d13c9 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 7 Sep 2020 11:02:20 -0400 Subject: [PATCH 10/11] dev: clean up CrossRuby and prepare for darwin verify_dll --- tasks/cross-ruby.rb | 62 +++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/tasks/cross-ruby.rb b/tasks/cross-ruby.rb index 411053461be..e7c63fbef6a 100644 --- a/tasks/cross-ruby.rb +++ b/tasks/cross-ruby.rb @@ -1,4 +1,21 @@ CrossRuby = Struct.new(:version, :host) do + WINDOWS_PLATFORM_REGEX = /mingw|mswin/ + MINGW32_PLATFORM_REGEX = /mingw32/ + LINUX_PLATFORM_REGEX = /linux/ + DARWIN_PLATFORM_REGEX = /darwin/ + + def windows? + !!(platform =~ WINDOWS_PLATFORM_REGEX) + end + + def linux? + !!(platform =~ LINUX_PLATFORM_REGEX) + end + + def darwin? + !!(platform =~ DARWIN_PLATFORM_REGEX) + end + def ver @ver ||= version[/\A[^-]+/] end @@ -10,7 +27,7 @@ def minor_ver def api_ver_suffix case minor_ver when nil - raise "unsupported version: #{ver}" + raise "CrossRuby.api_ver_suffix: unsupported version: #{ver}" else minor_ver.delete(".") << "0" end @@ -29,27 +46,10 @@ def platform when /\Ax86_64-darwin19/ "x86_64-darwin19" else - raise "unsupported host: #{host}" + raise "CrossRuby.platform: unsupported host: #{host}" end end - WINDOWS_PLATFORM_REGEX = /mingw|mswin/ - MINGW32_PLATFORM_REGEX = /mingw32/ - LINUX_PLATFORM_REGEX = /linux/ - DARWIN_PLATFORM_REGEX = /darwin/ - - def windows? - !!(platform =~ WINDOWS_PLATFORM_REGEX) - end - - def linux? - !!(platform =~ LINUX_PLATFORM_REGEX) - end - - def darwin? - !!(platform =~ DARWIN_PLATFORM_REGEX) - end - def tool(name) (@binutils_prefix ||= case platform when "x64-mingw32" @@ -60,17 +60,21 @@ def tool(name) "x86_64-linux-gnu-" when "x86-linux" "i686-linux-gnu-" - else + when /darwin/ "" + else + raise "CrossRuby.tool: unmatched platform: #{platform}" end) + name end - def target + def target_file_format case platform when "x64-mingw32" "pei-x86-64" when "x86-mingw32" "pei-i386" + else + raise "CrossRuby.target_file_format: unmatched platform: #{platform}" end end @@ -88,6 +92,8 @@ def libruby_dll "x64-msvcrt-ruby#{api_ver_suffix}.dll" when "x86-mingw32" "msvcrt-ruby#{api_ver_suffix}.dll" + else + raise "CrossRuby.libruby_dll: unmatched platform: #{platform}" end end @@ -99,9 +105,9 @@ def dlls "msvcrt.dll", "ws2_32.dll", *(case - when ver >= "2.0.0" - "user32.dll" - end), + when ver >= "2.0.0" + "user32.dll" + end), libruby_dll, ] when LINUX_PLATFORM_REGEX @@ -113,6 +119,10 @@ def dlls end), "libc.so.6", ] + when DARWIN_PLATFORM_REGEX + [] + else + raise "CrossRuby.dlls: unmatched platform: #{platform}" end end @@ -120,6 +130,8 @@ def dll_ref_versions case platform when LINUX_PLATFORM_REGEX { "GLIBC" => "2.17" } + else + raise "CrossRuby.dll_ref_versions: unmatched platform: #{platform}" end end end @@ -140,7 +152,7 @@ def verify_dll(dll, cross_ruby) dump = `#{["env", "LANG=C", cross_ruby.tool("objdump"), "-p", dll].shelljoin}` if cross_ruby.windows? - raise "unexpected file format for generated dll #{dll}" unless /file format #{Regexp.quote(cross_ruby.target)}\s/ === dump + raise "unexpected file format for generated dll #{dll}" unless /file format #{Regexp.quote(cross_ruby.target_file_format)}\s/ === dump raise "export function Init_nokogiri not in dll #{dll}" unless /Table.*\sInit_nokogiri\s/mi === dump # Verify that the expected DLL dependencies match the actual dependencies From 9f19c7095692197dc6b0501d49aa4ddf642e6357 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 7 Sep 2020 11:25:07 -0400 Subject: [PATCH 11/11] gem: improve verify_dll so linux does same checks as windows --- tasks/cross-ruby.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tasks/cross-ruby.rb b/tasks/cross-ruby.rb index e7c63fbef6a..bcb27e8feaf 100644 --- a/tasks/cross-ruby.rb +++ b/tasks/cross-ruby.rb @@ -73,6 +73,10 @@ def target_file_format "pei-x86-64" when "x86-mingw32" "pei-i386" + when "x86_64-linux" + "elf64-x86-64" + when "x86-linux" + "elf32-i386" else raise "CrossRuby.target_file_format: unmatched platform: #{platform}" end @@ -149,9 +153,10 @@ def dll_ref_versions def verify_dll(dll, cross_ruby) expected_imports = cross_ruby.dlls.sort - dump = `#{["env", "LANG=C", cross_ruby.tool("objdump"), "-p", dll].shelljoin}` if cross_ruby.windows? + dump = `#{["env", "LANG=C", cross_ruby.tool("objdump"), "-p", dll].shelljoin}` + raise "unexpected file format for generated dll #{dll}" unless /file format #{Regexp.quote(cross_ruby.target_file_format)}\s/ === dump raise "export function Init_nokogiri not in dll #{dll}" unless /Table.*\sInit_nokogiri\s/mi === dump @@ -163,6 +168,12 @@ def verify_dll(dll, cross_ruby) end elsif cross_ruby.linux? + dump = `#{["env", "LANG=C", cross_ruby.tool("objdump"), "-p", dll].shelljoin}` + nm = `#{["env", "LANG=C", cross_ruby.tool("nm"), "-D", dll].shelljoin}` + + raise "unexpected file format for generated dll #{dll}" unless /file format #{Regexp.quote(cross_ruby.target_file_format)}\s/ === dump + raise "export function Init_nokogiri not in dll #{dll}" unless / T Init_nokogiri/ === nm + # Verify that the expected so dependencies match the actual dependencies # and that no further dependencies exist. actual_imports = dump.scan(/NEEDED\s+(.*)/).map(&:first).uniq.sort