Skip to content

Commit

Permalink
add native dlopen extension code
Browse files Browse the repository at this point in the history
This will get dlopen in a consistent place on all the platforms
which need it.
  • Loading branch information
lamont-granquist committed Apr 6, 2015
1 parent 10c7da3 commit 9a53602
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 53 deletions.
7 changes: 7 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ Rake::ExtensionTask.new do |ext|
ext.gem_spec = spec
end

Rake::ExtensionTask.new do |ext|
ext.name = 'dlopen'
ext.lib_dir = 'lib/ffi_yajl/ext'
ext.ext_dir = 'ext/ffi_yajl/ext/dlopen'
ext.gem_spec = spec
end

#
# test tasks
#
Expand Down
38 changes: 38 additions & 0 deletions ext/ffi_yajl/ext/dlopen/dlopen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <ruby.h>

#if defined(HAVE_DLFCN_H)
# include <dlfcn.h>
#ifndef RTLD_LAZY
#define RTLD_LAZY 0
#endif
#ifndef RTLD_GLOBAL
#define RTLD_GLOBAL 0
#endif
#ifndef RTLD_NOW
#define RTLD_NOW 0
#endif
#else
# if defined(_WIN32)
# include <windows.h>
# define dlopen(name,flag) ((void*)LoadLibrary(name))
# define dlerror() strerror(rb_w32_map_errno(GetLastError()))
# define dlsym(handle,name) ((void*)GetProcAddress((handle),(name)))
# define RTLD_LAZY -1
# define RTLD_NOW -1
# define RTLD_GLOBAL -1
# endif
#endif

static VALUE mFFI_Yajl, mDlopen, mExt;

static VALUE mDlopen_dlopen(VALUE self, VALUE file) {
dlopen(RSTRING_PTR(file), RTLD_NOW|RTLD_GLOBAL);
return Qnil;
}

void Init_dlopen() {
mFFI_Yajl = rb_define_module("FFI_Yajl");
mExt = rb_define_module_under(mFFI_Yajl, "Ext");
mDlopen = rb_define_module_under(mExt, "Dlopen");
rb_define_method(mDlopen, "dlopen", mDlopen_dlopen, 1);
}
15 changes: 15 additions & 0 deletions ext/ffi_yajl/ext/dlopen/extconf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require 'mkmf'
require 'rubygems'

RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']

puts $CFLAGS
puts $LDFLAGS

have_header("dlfcn.h")

have_library("dl", "dlopen")

dir_config 'dlopen'

create_makefile 'ffi_yajl/ext/dlopen'
3 changes: 1 addition & 2 deletions ffi-yajl.gemspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
gemspec = eval(IO.read(File.expand_path(File.join(File.dirname(__FILE__), "ffi-yajl.gemspec.shared"))))

gemspec.platform = Gem::Platform::RUBY
gemspec.extensions = %w{ ext/ffi_yajl/ext/encoder/extconf.rb ext/ffi_yajl/ext/parser/extconf.rb }
gemspec.extensions = %w{ ext/ffi_yajl/ext/encoder/extconf.rb ext/ffi_yajl/ext/parser/extconf.rb ext/ffi_yajl/ext/dlopen/extconf.rb }

gemspec

8 changes: 4 additions & 4 deletions lib/ffi_yajl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
require 'ffi_yajl/ffi'
elsif RUBY_PLATFORM == "java"
require 'ffi_yajl/ffi'
elsif defined?(Yajl)
warn "the ffi-yajl and yajl-ruby gems have incompatible C libyajl libs and should not be loaded in the same Ruby VM"
warn "falling back to ffi which might work (or might not, no promises)"
require 'ffi_yajl/ffi'
#elsif defined?(Yajl)
# warn "the ffi-yajl and yajl-ruby gems have incompatible C libyajl libs and should not be loaded in the same Ruby VM"
# warn "falling back to ffi which might work (or might not, no promises)"
# require 'ffi_yajl/ffi'
else
begin
require 'ffi_yajl/ext'
Expand Down
51 changes: 4 additions & 47 deletions lib/ffi_yajl/ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,17 @@
require 'ffi_yajl/parser'
require 'libyajl2'
require 'ffi_yajl/map_library_name'
require 'ffi_yajl/ext/dlopen'

module FFI_Yajl
extend FFI_Yajl::MapLibraryName

extend FFI_Yajl::Ext::Dlopen

libname = map_library_name
libpath = File.expand_path(File.join(Libyajl2.opt_path, libname))

#
# FFS, what exactly was so wrong with DL.dlopen that ruby had to get rid of it???
#

def self.try_fiddle_dlopen(libpath)
require 'fiddle'
if defined?(Fiddle) && Fiddle.respond_to?(:dlopen)
::Fiddle.dlopen(libpath)
true
else
false
end
rescue LoadError
return false
end

def self.try_dl_dlopen(libpath)
require 'dl'
if defined?(DL) && DL.respond_to?(:dlopen)
::DL.dlopen(libpath)
true
else
false
end
rescue LoadError
return false
end

def self.try_ffi_dlopen(libpath)
require 'ffi'
require 'rbconfig'
extend ::FFI::Library
ffi_lib 'dl'
attach_function 'dlopen', :dlopen, [:string, :int], :void
if RbConfig::CONFIG['host_os'] =~ /linux/i
dlopen libpath, 0x102 # linux: RTLD_GLOBAL | RTLD_NOW
else
dlopen libpath, 0
end
true
rescue LoadError
return false
end

unless try_fiddle_dlopen(libpath) || try_dl_dlopen(libpath) || try_ffi_dlopen(libpath)
raise "cannot find dlopen via Fiddle, DL or FFI, what am I supposed to do?"
end
dlopen(libpath)

class Parser
require 'ffi_yajl/ext/parser'
Expand Down

0 comments on commit 9a53602

Please sign in to comment.