Skip to content

Commit

Permalink
Merge pull request #373 from larskanis/lib-path
Browse files Browse the repository at this point in the history
Make the library path of libpq available in ruby and add it to the DLL search paths on Windows which lacks rpath.
  • Loading branch information
larskanis authored Mar 29, 2021
2 parents c8544bc + 2f6ab32 commit b9daa27
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 29 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ source "https://rubygems.org/"
gem "hoe-mercurial", "~>1.4", :group => [:development, :test]
gem "hoe-deveiate", "~>0.9", :group => [:development, :test]
gem "hoe-highline", "~>0.2", :group => [:development, :test]
gem "rake-compiler", "~>1.0", :group => [:development, :test]
gem "rake-compiler", "1.1.1", :group => [:development, :test]
gem "rake-compiler-dock", "~>1.0", :group => [:development, :test]
gem "hoe-bundler", "~>1.0", :group => [:development, :test]
gem "rspec", "~>3.5", :group => [:development, :test]
Expand Down
4 changes: 3 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require 'tmpdir'

begin
require 'rake/extensiontask'
require_relative 'misc/rake-compiler-make-install-patch'
rescue LoadError
abort "This Rakefile requires rake-compiler (gem install rake-compiler)"
end
Expand Down Expand Up @@ -37,6 +38,7 @@ CLOBBER.include( TESTDIR.to_s )
CLEAN.include( PKGDIR.to_s, TMPDIR.to_s )
CLEAN.include "lib/*/libpq.dll"
CLEAN.include "lib/pg_ext.*"
CLEAN.include "lib/pg/postgresql_lib_path.rb"

# Set up Hoe plugins
Hoe.plugin :mercurial
Expand All @@ -62,7 +64,7 @@ $hoespec = Hoe.spec 'pg' do
self.developer 'Michael Granger', 'ged@FaerieMUD.org'
self.developer 'Lars Kanis', 'lars@greiz-reinsdorf.de'

self.dependency 'rake-compiler', '~> 1.0', :developer
self.dependency 'rake-compiler', '1.1.1', :developer
self.dependency 'rake-compiler-dock', ['~> 1.0'], :developer
self.dependency 'hoe-deveiate', '~> 0.9', :developer
self.dependency 'hoe-bundler', '~> 1.0', :developer
Expand Down
1 change: 0 additions & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ install:
$(new-object net.webclient).DownloadFile('http://get.enterprisedb.com/postgresql/postgresql-' + $env:PGVERSION + '.exe', 'C:/postgresql-setup.exe')
cmd /c "C:/postgresql-setup.exe" --mode unattended --extract-only 1
}
$env:RUBY_DLL_PATH = 'C:/Program Files/PostgreSQL/' + $env:PGVER + '/bin;C:/Program Files (x86)/PostgreSQL/' + $env:PGVER + '/bin'
$env:PATH = 'C:/Program Files/PostgreSQL/' + $env:PGVER + '/bin;' + $env:PATH
$env:PATH = 'C:/Program Files (x86)/PostgreSQL/' + $env:PGVER + '/bin;' + $env:PATH
build_script:
Expand Down
28 changes: 21 additions & 7 deletions ext/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,33 @@
libdir = `"#{pgconfig}" --libdir`.chomp
dir_config 'pg', incdir, libdir

# Try to use runtime path linker option, even if RbConfig doesn't know about it.
# The rpath option is usually set implicit by dir_config(), but so far not
# on MacOS-X.
if RbConfig::CONFIG["RPATHFLAG"].to_s.empty? && try_link('int main() {return 0;}', " -Wl,-rpath,#{libdir}")
$LDFLAGS << " -Wl,-rpath,#{libdir}"
end
# Windows traditionally stores DLLs beside executables, not in libdir
dlldir = RUBY_PLATFORM=~/mingw|mswin/ ? `"#{pgconfig}" --bindir`.chomp : libdir

else
$stderr.puts "No pg_config... trying anyway. If building fails, please try again with",
" --with-pg-config=/path/to/pg_config"
dir_config 'pg'
incdir, libdir = dir_config 'pg'
dlldir = libdir
end

# Try to use runtime path linker option, even if RbConfig doesn't know about it.
# The rpath option is usually set implicit by dir_config(), but so far not
# on MacOS-X.
if dlldir && RbConfig::CONFIG["RPATHFLAG"].to_s.empty?
append_ldflags "-Wl,-rpath,#{dlldir.quote}"
end
end

File.write("postgresql_lib_path.rb", <<-EOT)
module PG
POSTGRESQL_LIB_PATH = #{dlldir.inspect}
end
EOT
$INSTALLFILES = {
"./postgresql_lib_path.rb" => "$(RUBYLIBDIR)/pg/"
}

if RUBY_VERSION >= '2.3.0' && /solaris/ =~ RUBY_PLATFORM
append_cppflags( '-D__EXTENSIONS__' )
end
Expand Down
51 changes: 33 additions & 18 deletions lib/pg.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@

# -*- ruby -*-
# frozen_string_literal: true

begin
require 'pg_ext'
rescue LoadError
# If it's a Windows binary gem, try the <major>.<minor> subdirectory
if RUBY_PLATFORM =~/(mswin|mingw)/i
major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
# The top-level PG namespace.
module PG

# Is this file part of a fat binary gem with bundled libpq?
bundled_libpq_path = File.join(__dir__, RUBY_PLATFORM.gsub(/^i386-/, "x86-"))
if File.exist?(bundled_libpq_path)
POSTGRESQL_LIB_PATH = bundled_libpq_path
else
bundled_libpq_path = nil
# Try to load libpq path as found by extconf.rb
begin
require "pg/postgresql_lib_path"
rescue LoadError
# rake-compiler doesn't use regular "make install", but uses it's own install tasks.
# It therefore doesn't copy pg/postgresql_lib_path.rb in case of "rake compile".
POSTGRESQL_LIB_PATH = false
end
end

add_dll_path = proc do |path, &block|
add_dll_path = proc do |path, &block|
if RUBY_PLATFORM =~/(mswin|mingw)/i && path && File.exist?(path)
begin
require 'ruby_installer/runtime'
RubyInstaller::Runtime.add_dll_directory(path, &block)
Expand All @@ -19,22 +32,24 @@
block.call
ENV['PATH'] = old_path
end
else
# No need to set a load path manually - it's set as library rpath.
block.call
end
end

# Temporary add this directory for DLL search, so that libpq.dll can be found.
# mingw32-platform strings differ (RUBY_PLATFORM=i386-mingw32 vs. x86-mingw32 for rubygems)
add_dll_path.call(File.join(__dir__, RUBY_PLATFORM.gsub(/^i386-/, "x86-"))) do
# Add a load path to the one retrieved from pg_config
add_dll_path.call(POSTGRESQL_LIB_PATH) do
if bundled_libpq_path
# It's a Windows binary gem, try the <major>.<minor> subdirectory
major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
require "#{major_minor}/pg_ext"
else
require 'pg_ext'
end
else
raise
end

end


# The top-level PG namespace.
module PG

# Library version
VERSION = '1.2.3'
Expand Down
153 changes: 153 additions & 0 deletions misc/rake-compiler-make-install-patch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
require 'rake/baseextensiontask'

module Rake
class ExtensionTask < BaseExtensionTask

# Replace method
undef define_compile_tasks

def define_compile_tasks(for_platform = nil, ruby_ver = RUBY_VERSION)
# platform usage
platf = for_platform || platform

binary_path = binary(platf)

# lib_path
lib_path = lib_dir

lib_binary_path = "#{lib_path}/#{binary_path}"
lib_binary_dir_path = File.dirname(lib_binary_path)

# tmp_path
tmp_path = "#{@tmp_dir}/#{platf}/#{@name}/#{ruby_ver}"
stage_path = "#{@tmp_dir}/#{platf}/stage"

siteconf_path = "#{tmp_path}/.rake-compiler-siteconf.rb"
tmp_binary_path = "#{tmp_path}/#{binary_path}"
tmp_binary_dir_path = File.dirname(tmp_binary_path)
stage_binary_path = "#{stage_path}/#{lib_path}/#{binary_path}"
stage_binary_dir_path = File.dirname(stage_binary_path)

# cleanup and clobbering
CLEAN.include(tmp_path)
CLEAN.include(stage_path)
CLOBBER.include("#{lib_path}/#{binary(platf)}")
CLOBBER.include("#{@tmp_dir}")

# directories we need
directory tmp_path
directory tmp_binary_dir_path
directory lib_binary_dir_path
directory stage_binary_dir_path

directory File.dirname(siteconf_path)
# Set paths for "make install" destinations
file siteconf_path => File.dirname(siteconf_path) do
File.open(siteconf_path, "w") do |siteconf|
siteconf.puts "require 'rbconfig'"
siteconf.puts "require 'mkmf'"
siteconf.puts "dest_path = mkintpath(#{File.expand_path(lib_path).dump})"
%w[sitearchdir sitelibdir].each do |dir|
siteconf.puts "RbConfig::MAKEFILE_CONFIG['#{dir}'] = dest_path"
siteconf.puts "RbConfig::CONFIG['#{dir}'] = dest_path"
end
end
end

# copy binary from temporary location to final lib
# tmp/extension_name/extension_name.{so,bundle} => lib/
task "copy:#{@name}:#{platf}:#{ruby_ver}" => [lib_binary_dir_path, tmp_binary_path, "#{tmp_path}/Makefile"] do
# install in lib for native platform only
unless for_platform
sh "#{make} install", chdir: tmp_path
end
end
# copy binary from temporary location to staging directory
task "copy:#{@name}:#{platf}:#{ruby_ver}" => [stage_binary_dir_path, tmp_binary_path] do
cp tmp_binary_path, stage_binary_path
end

# copy other gem files to staging directory
define_staging_file_tasks(@gem_spec.files, lib_path, stage_path, platf, ruby_ver) if @gem_spec

# binary in temporary folder depends on makefile and source files
# tmp/extension_name/extension_name.{so,bundle}
file tmp_binary_path => [tmp_binary_dir_path, "#{tmp_path}/Makefile"] + source_files do
jruby_compile_msg = <<-EOF
Compiling a native C extension on JRuby. This is discouraged and a
Java extension should be preferred.
EOF
warn_once(jruby_compile_msg) if defined?(JRUBY_VERSION)

chdir tmp_path do
sh make
if binary_path != File.basename(binary_path)
cp File.basename(binary_path), binary_path
end
end
end

# makefile depends of tmp_dir and config_script
# tmp/extension_name/Makefile
file "#{tmp_path}/Makefile" => [tmp_path, extconf, siteconf_path] do |t|
options = @config_options.dup

# include current directory
include_dirs = ['.'].concat(@config_includes).uniq.join(File::PATH_SEPARATOR)
cmd = [Gem.ruby, "-I#{include_dirs}", "-r#{File.basename(siteconf_path)}"]

# build a relative path to extconf script
abs_tmp_path = (Pathname.new(Dir.pwd) + tmp_path).realpath
abs_extconf = (Pathname.new(Dir.pwd) + extconf).realpath

# now add the extconf script
cmd << abs_extconf.relative_path_from(abs_tmp_path)

# fake.rb will be present if we are cross compiling
if t.prerequisites.include?("#{tmp_path}/fake.rb") then
options.push(*cross_config_options(platf))
end

# add options to command
cmd.push(*options)

# add any extra command line options
unless extra_options.empty?
cmd.push(*extra_options)
end

chdir tmp_path do
# FIXME: Rake is broken for multiple arguments system() calls.
# Add current directory to the search path of Ruby
sh cmd.join(' ')
end
end

# compile tasks
unless Rake::Task.task_defined?('compile') then
desc "Compile all the extensions"
task "compile"
end

# compile:name
unless Rake::Task.task_defined?("compile:#{@name}") then
desc "Compile #{@name}"
task "compile:#{@name}"
end

# Allow segmented compilation by platform (open door for 'cross compile')
task "compile:#{@name}:#{platf}" => ["copy:#{@name}:#{platf}:#{ruby_ver}"]
task "compile:#{platf}" => ["compile:#{@name}:#{platf}"]

# Only add this extension to the compile chain if current
# platform matches the indicated one.
if platf == RUBY_PLATFORM then
# ensure file is always copied
file "#{lib_path}/#{binary_path}" => ["copy:#{name}:#{platf}:#{ruby_ver}"]

task "compile:#{@name}" => ["compile:#{@name}:#{platf}"]
task "compile" => ["compile:#{platf}"]
end
end
end
end
5 changes: 4 additions & 1 deletion spec/pg_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,8 @@
])
end

end
it "tells about the libpq library path" do
expect( PG::POSTGRESQL_LIB_PATH ).to include("/")
end

end

0 comments on commit b9daa27

Please sign in to comment.