From b2a2e807133c7e6ed28635da751b0bb109950417 Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Sun, 18 Jul 2021 23:22:11 +0900 Subject: [PATCH] WIP --- lib/iruby/zmq.rb | 4 ++ lib/iruby/zmq/libzmq.rb | 111 +++++++++++++++++++++++++++++++++ lib/iruby/zmq/libzmq_finder.rb | 93 +++++++++++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 lib/iruby/zmq.rb create mode 100644 lib/iruby/zmq/libzmq.rb create mode 100644 lib/iruby/zmq/libzmq_finder.rb diff --git a/lib/iruby/zmq.rb b/lib/iruby/zmq.rb new file mode 100644 index 0000000..dacd821 --- /dev/null +++ b/lib/iruby/zmq.rb @@ -0,0 +1,4 @@ +module IRuby + module ZMQ + end +end diff --git a/lib/iruby/zmq/libzmq.rb b/lib/iruby/zmq/libzmq.rb new file mode 100644 index 0000000..4b5c2de --- /dev/null +++ b/lib/iruby/zmq/libzmq.rb @@ -0,0 +1,111 @@ +require_relative "libzmq_finder" + +module IRuby + module ZMQ + module LibZMQ + # Socket types. + ZMQ_PUB = 1 + ZMQ_DEALER = 5 + ZMQ_ROUTER = 6 + ZMQ_PULL = 7 + ZMQ_PUSH = 8 + ZMQ_STREAM = 11 + + # Socket options. + ZMQ_ROUTING_ID = 5 + ZMQ_FD = 14 + ZMQ_LINGER = 17 + ZMQ_LAST_ENDPOINT = 32 + + # Send/recv options. + ZMQ_DONTWAIT = 1 + ZMQ_SNDMORE = 2 + + def self.load + path = LibZMQFinder.find_libzmq + if path + @path = path + import_library(@path) + define_version_constant + true + else + raise LoadError, "Unable to find libzmq" + end + end + + private_class_method def self.import_library(path) + require "fiddle/import" + extend Fiddle::Importer + dlload @path + + extern "int zmq_errno(void)" + extern "const char *zmq_strerror(int errnum_)" + + extern "void zmq_version(int *major_, int *minor_, int *patch_)" + + extern "void *zmq_ctx_new(void)" + extern "int zmq_ctx_term(void *context_)" + + extern "void *zmq_socket(void *, int type_)" + extern "int zmq_close(void *s_)" + extern "int zmq_setsockopt(void *s_, int option_, const void *optval_, size_t *optvallen_)" + extern "int zmq_getsockopt(void *s_, int option_, void *optval_, size_t *optvallen_)" + extern "int zmq_bind(void *s_, const char *addr_)" + extern "int zmq_connect(void *s_, const char *addr_)" + extern "int zmq_send(void *s_, const void *buf_, size_t len_, int flags_)" + extern "int zmq_recv(void *s_, const void *buf_, size_t len_, int flags_)" + + extern "int zmq_proxy(void *frontend_, void *backend_, void *capture_)" + + if windows? + typealias "zmq_fd_t", "uintptr_t" # zmq_fd_t = SOCKET = UINT_PTR + else + typealias "zmq_fd_t", "int" + end + + begin + extern "void *zmq_poller_new()" + + zmq_poller_event_t = struct([ + "void *socket", + "zmq_fd_t fd", + "void *user_data", + "short events" + ]) + const_set :ZMQ_PollerEvent, zmq_poller_event_t + + extern "int zmq_poller_destroy(void **poller_p)" + extern "int zmq_poller_add(void *poller, void *socket, void *user_data, short events)" + extern "int zmq_poller_remove(void *poller, void *socket)" + extern "int zmq_poller_wait(void *poller, void *event, long timeout)" + extern "int zmq_poller_wait_all(void *poller, void *events, int n_events, long timeout)" + rescue Fiddle::DLError + @poller_available = false + else + @poller_available = true + end + end + + private_class_method def self.define_version_constant + major_p = value("int") + minor_p = value("int") + patch_p = value("int") + zmq_version(major_p, minor_p, patch_p) + const_set :Version, Module.new + Version.const_set :MAJOR, major_p.value + Version.const_set :MINOR, minor_p.value + Version.const_set :PATCH, patch_p.value + Version.const_set :STRING, [major_p, minor_p, patch_p].map(&:value).join(".") + const_set :VERSION, Version::STRING + end + + def self.poller_available? + @poller_available + end + + def self.windows? + /mingw|mswin|msys/ =~ RUBY_PLATFORM + end + end + end +end diff --git a/lib/iruby/zmq/libzmq_finder.rb b/lib/iruby/zmq/libzmq_finder.rb new file mode 100644 index 0000000..db051bd --- /dev/null +++ b/lib/iruby/zmq/libzmq_finder.rb @@ -0,0 +1,93 @@ +require "fiddle" +require "open3" +require "rbconfig" + +module IRuby + module ZMQ + module LibZMQFinder + def self.find_libzmq + name_candidates = list_candidates + + # 1. Try IRUBY_LIBZMQ environment variable + if ENV.key?("IRUBY_LIBZMQ") + begin + Fiddle.dlopen(ENV["IRUBY_LIBZMQ"]).close + rescue Fiddle::LoadError + warn "WARNING: Unable to load libzmq specified in IRUBY_LIBZMQ" + else + return ENV["IRUBY_LIBZMQ"] + end + end + + # 2. Try IRUBY_LIBZMQ_PREFIX environment variable + if ENV.key?("IRUBY_LIBZMQ_PREFIX") + prefix = ENV["IRUBY_LIBZMQ_PREFIX"] + if File.directory?(prefix) + libdir = File.join(prefix, "lib") + path = find_libzmq_at(name_candidates, libdir) + return path if path + warn "WARNING: Unable to load libzmq in ${IRUBY_LIBZMQ_PREFIX}/lib" + else + warn "WARNING: IRUBY_LIBZMQ_PREFIX is specified, but it is not a directory" + end + end + + # 3. Try Homebrew if available + path = find_libzmq_in_homebrew(name_candidates) + return path if path + + # 4. Search in the default library path + return name_candidates.find {|fn| loadable?(fn) } + end + + def self.find_libzmq_at(name_candidates, dir) + name_candidates.each do |name| + path = File.join(dir, name) + next unless File.file?(path) + return path if loadable?(path) + end + return nil + end + + def self.find_libzmq_in_homebrew(name_candidates) + begin + out, status = Open3.capture2("brew", "--prefix", "zmq", err: File::NULL) + return nil unless status.success? + libdir = File.join(out.chomp, "lib") + File.directory?(libdir) && find_libzmq_at(name_candidates, libdir) + rescue Errno::ENOENT + return nil + end + end + + def self.loadable?(filename) + begin + $stderr.puts "loadable?(%p)" % filename + Fiddle.dlopen(filename).close + rescue Fiddle::DLError + false + else + true + end + end + + def self.list_candidates + name_stem = ["zmq"] + name_prefix = ["lib"] + name_ext = [".#{RbConfig::CONFIG["SOEXT"]}"] + + # Assume version 4.x + version_suffix = [".5", ""] + + # Examine non-"lib"-prefixed name on Windows + name_prefix << "" if /mswin|mingw|msys/ =~ RUBY_PLATFORM + + if /darwin|mac os/ =~ RUBY_PLATFORM + name_prefix.product(name_stem, version_suffix, name_ext).map(&:join) + else + name_prefix.product(name_stem, name_ext, version_suffix).map(&:join) + end + end + end + end +end