From 6207ce74f6ef65b543723a97e2adf2adfe7eecb2 Mon Sep 17 00:00:00 2001 From: Andy Pfister Date: Fri, 3 Jan 2025 09:19:22 +0100 Subject: [PATCH] Provide precompiled gem for Linux --- .github/workflows/ci.yml | 80 +++++++++++++++++++++++----------------- CHANGELOG.md | 4 ++ Rakefile | 10 +++++ lib/tiny_tds.rb | 61 ++++++------------------------ 4 files changed, 73 insertions(+), 82 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a603b679..bfe677dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,9 @@ jobs: platform: - "x64-mingw32" - "x64-mingw-ucrt" - name: cross-compile-windows + - "x86_64-linux-gnu" + + name: cross-compile runs-on: ubuntu-22.04 container: image: "ghcr.io/rake-compiler/rake-compiler-dock-image:1.7.0-mri-${{ matrix.platform }}" @@ -43,6 +45,9 @@ jobs: shell: bash run: bundle exec rake gem:for_platform[${{ matrix.platform }}] + - name: Remove non-precompiled gem + run: "rm -f tiny_tds-$(cat VERSION).gem" + - uses: actions/upload-artifact@v4 with: name: gem-${{ matrix.platform }} @@ -336,36 +341,38 @@ jobs: ruby -e "require 'tiny_tds'; puts TinyTds::Gem.root_path" exit $LASTEXITCODE - compile-native-ports: + install-linux: + needs: + - cross-compile + strategy: + fail-fast: false + matrix: + platform: + - "x86_64-linux-gnu" + + ruby-version: + - "2.7" + - "3.0" + - "3.1" + - "3.2" + - "3.3" + - "3.4" + runs-on: ubuntu-22.04 - name: cross-compile-linux steps: - uses: actions/checkout@v4 - - uses: ruby/setup-ruby@v1 + - uses: actions/download-artifact@v4 with: - ruby-version: 3.4 - bundler-cache: true - - - name: Write used versions into file - run: bundle exec rake ports:version_file - - - name: Cache ports - uses: actions/cache@v4 - with: - path: ports - key: native-v3-${{ hashFiles('**/.ports_versions') }} - restore-keys: | - native-v3-${{ hashFiles('* */.ports_versions') }} - native-v3- + name: gem-${{ matrix.platform }} + path: precompiled - - name: Build required libraries - run: | - bundle exec rake ports + - run: | + docker run --rm -v $PWD/precompiled:/precompiled -w /precompiled ${{ matrix.docker-platform }} ruby:${{ matrix.ruby-version }}${{ matrix.docker-tag }} sh -c "ls -lstar && gem update --system && gem install --no-document $(ls *.gem | head -n1) && ruby -e \"require 'tiny_tds'; puts TinyTds::Gem.root_path\"" test-linux: needs: - - compile-native-ports + - cross-compile name: test-linux strategy: fail-fast: false @@ -391,20 +398,27 @@ jobs: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true - - name: Write used versions into file - run: | - bundle exec rake ports:version_file - - - name: Cache ports - uses: actions/cache@v4 + - name: Download precompiled gem + uses: actions/download-artifact@v4 with: - path: ports - key: native-v3-${{ hashFiles('**/.ports_versions') }} - fail-on-cache-miss: true + name: gem-x86_64-linux-gnu - - name: Build gem + - name: Install native gem and restore cross-compiled code from it + shell: pwsh run: | - bundle exec rake build + $gemVersion = (Get-Content VERSION).Trim() + $gemToUnpack = "./tiny_tds-$gemVersion-x86_64-linux-gnu.gem" + + Write-Host "Looking to unpack $gemToUnpack" + gem unpack --target ./tmp "$gemToUnpack" + + # Restore precompiled code + $source = (Resolve-Path ".\tmp\tiny_tds-$gemVersion-x86_64-linux-gnu\lib\tiny_tds").Path + $destination = (Resolve-Path ".\lib\tiny_tds").Path + Get-ChildItem $source -Recurse -Exclude "*.rb" | Copy-Item -Destination {Join-Path $destination $_.FullName.Substring($source.length)} + + # Restore ports + Copy-Item -Path ".\tmp\tiny_tds-$gemVersion-x86_64-linux-gnu\ports" -Destination "." -Recurse - name: Setup MSSQL uses: rails-sqlserver/setup-mssql@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3128e1ec..c7e4be13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## (unreleased) + +* Provide precompiled gem for Linux + ## 3.1.0 * Add Ruby 3.4 to the cross compile list diff --git a/Rakefile b/Rakefile index 9999bff6..f318ce02 100644 --- a/Rakefile +++ b/Rakefile @@ -9,6 +9,7 @@ SPEC = Gem::Specification.load(File.expand_path('../tiny_tds.gemspec', __FILE__) ruby_cc_ucrt_versions = "3.4.0:3.3.5:3.2.0:3.1.0".freeze ruby_cc_mingw32_versions = "3.0.0:2.7.0".freeze +ruby_cc_all_supported_versions = "#{ruby_cc_ucrt_versions}:#{ruby_cc_mingw32_versions}" GEM_PLATFORM_HOSTS = { 'x64-mingw32' => { @@ -19,6 +20,10 @@ GEM_PLATFORM_HOSTS = { host: 'x86_64-w64-mingw32', ruby_versions: ruby_cc_ucrt_versions }, + 'x86_64-linux-gnu' => { + host: 'x86_64-linux-gnu', + ruby_versions: ruby_cc_all_supported_versions + }, } # Add our project specific files to clean for a rebuild @@ -51,6 +56,11 @@ Rake::ExtensionTask.new('tiny_tds', SPEC) do |ext| end spec.files += Dir.glob('exe/*') + + if spec.platform.os == "linux" + # with this release, "-gnu" refers to glibc-based Linux and "-musl" for musl + spec.required_rubygems_version.concat([">= 3.3.22"]) + end end end diff --git a/lib/tiny_tds.rb b/lib/tiny_tds.rb index 52247915..5825b688 100644 --- a/lib/tiny_tds.rb +++ b/lib/tiny_tds.rb @@ -9,53 +9,16 @@ require 'tiny_tds/result' require 'tiny_tds/gem' -# Support multiple ruby versions, fat binaries under Windows. -if RUBY_PLATFORM =~ /mingw|mswin/ && RUBY_VERSION =~ /(\d+.\d+)/ - ver = Regexp.last_match(1) - - add_dll_path = proc do |path, &block| - begin - require 'ruby_installer/runtime' - RubyInstaller::Runtime.add_dll_directory(path, &block) - rescue LoadError - old_path = ENV['PATH'] - ENV['PATH'] = "#{path};#{old_path}" - begin - block.call - ensure - ENV['PATH'] = old_path - end - end - end - - add_dll_paths = proc do |paths, &block| - if path=paths.shift - add_dll_path.call(path) do - add_dll_paths.call(paths, &block) - end - else - block.call - end - end - - # Temporary add bin directories for DLL search, so that freetds DLLs can be found. - add_dll_paths.call( TinyTds::Gem.ports_bin_paths ) do - begin - require "tiny_tds/#{ver}/tiny_tds" - rescue LoadError - require 'tiny_tds/tiny_tds' - end - end -else - # Load dependent shared libraries into the process, so that they are already present, - # when tiny_tds.so is loaded. This ensures, that shared libraries are loaded even when - # the path is different between build and run time (e.g. Heroku). - ports_libs = File.join(TinyTds::Gem.ports_root_path, - "#{RbConfig::CONFIG['host']}/lib/*.so") - Dir[ports_libs].each do |lib| - require 'fiddle' - Fiddle.dlopen(lib) - end - - require 'tiny_tds/tiny_tds' +begin + # load the precompiled extension file + ruby_version = /(\d+\.\d+)/.match(::RUBY_VERSION) + puts "tiny_tds/#{ruby_version}/tiny_tds" + require_relative "tiny_tds/#{ruby_version}/tiny_tds" +rescue LoadError + # fall back to the extension compiled upon installation. + # use "require" instead of "require_relative" because non-native gems will place C extension files + # in Gem::BasicSpecification#extension_dir after compilation (during normal installation), which + # is in $LOAD_PATH but not necessarily relative to this file + # (see https://github.com/sparklemotion/nokogiri/issues/2300 for more) + require "tiny_tds/tiny_tds" end