From c95b8f3664299ff60fa06073ab0ad77f4d5ed3d0 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 24 Jun 2022 21:32:38 +1200 Subject: Add support for passing ABI version to `FFI.map_library_name`. --- lib/ffi/library.rb | 59 +++++++++++++++++++++++++++++++++++++++++++--------- spec/ffi/ffi_spec.rb | 9 ++++++++ 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/lib/ffi/library.rb b/lib/ffi/library.rb index 43b2bfe..c2628ab 100644 --- a/lib/ffi/library.rb +++ b/lib/ffi/library.rb @@ -31,6 +31,53 @@ module FFI CURRENT_PROCESS = USE_THIS_PROCESS_AS_LIBRARY = Object.new + class LibraryPath < ::Struct.new(:name, :abi_number, :root) + PATTERN = /(#{Platform::LIBPREFIX})?(?.*?)(\.|\z)/ + + def self.wrap(value) + # We allow instances of LibraryPath to pass through transparently: + return value if value.is_a?(self) + + # We special case a library named 'c' to be the standard C library: + return Library::LIBC if value == 'c' + + # If provided a relative file name we convert it into a library path: + if value && File.basename(value) == value + if match = PATTERN.match(value) + return self.new(match[:name]) + end + end + + # Otherwise, we assume it's a full path to a library: + return value + end + + def full_name + # If the abi_number is given, we format it specifically according to platform rules: + if abi_number + if Platform.windows? + "#{Platform::LIBPREFIX}#{name}-#{abi_number}.#{Platform::LIBSUFFIX}" + elsif Platform.mac? + "#{Platform::LIBPREFIX}#{name}.#{abi_number}.#{Platform::LIBSUFFIX}" + else # Linux? BSD? etc. + "#{Platform::LIBPREFIX}#{name}.#{Platform::LIBSUFFIX}.#{abi_number}" + end + else + # Otherwise we just use a generic format: + "#{Platform::LIBPREFIX}#{name}.#{Platform::LIBSUFFIX}" + end + end + + def to_s + if root + # If the root path is given, we generate the full path: + File.join(root, full_name) + else + full_name + end + end + end + # @param [#to_s] lib library name # @return [String] library name formatted for current platform # Transform a generic library name to a platform library name @@ -41,17 +88,9 @@ module FFI # # Windows # FFI.map_library_name 'c' # -> "msvcrt.dll" # FFI.map_library_name 'jpeg' # -> "jpeg.dll" - def self.map_library_name(lib) + def self.map_library_name(value) # Mangle the library name to reflect the native library naming conventions - lib = Library::LIBC if lib == 'c' - - if lib && File.basename(lib) == lib - lib = Platform::LIBPREFIX + lib unless lib =~ /^#{Platform::LIBPREFIX}/ - r = Platform::IS_WINDOWS || Platform::IS_MAC ? "\\.#{Platform::LIBSUFFIX}$" : "\\.so($|\\.[1234567890]+)" - lib += ".#{Platform::LIBSUFFIX}" unless lib =~ /#{r}/ - end - - lib + LibraryPath.wrap(value).to_s end # Exception raised when a function is not found in libraries diff --git a/spec/ffi/ffi_spec.rb b/spec/ffi/ffi_spec.rb index 4de5310..608cd99 100644 --- a/spec/ffi/ffi_spec.rb +++ b/spec/ffi/ffi_spec.rb @@ -24,6 +24,15 @@ describe "FFI" do expect(FFI.map_library_name('c')).to eq(FFI::Library::LIBC) end + it "should return library path with abi version" do + expect(FFI.map_library_name(FFI::LibraryPath.new('vips', 42))).to be =~ /#{prefix}vips.*42/ + end + + it "should return library path with root" do + root = "/non/existant/root" + + expect(FFI.map_library_name(FFI::LibraryPath.new('vips', 42, root))).to be =~ /#{root}/#{prefix}vips.*42/ + end end describe "VERSION" do -- cgit v1.2.1