7

I installed some native libraries in /usr/local/lib.

I am now trying to install a gem which needs these in order to build correctly but, the gem build fails as it cannot find the libraries.

The gem's extconf.rb file tries to confirm it can find the library with have_library() but this fails for some reason.

I tried setting a bunch of environment variables but nothing seems to work:

irb(main):003:0> require 'mkmf'
=> true
irb(main):004:0> have_library('gecodesearch')
checking for main() in -lgecodesearch... no
=> false
irb(main):005:0> ENV['LD_LIBRARY_PATH']='/usr/local/lib'
=> "/usr/local/lib"
irb(main):006:0> have_library('gecodesearch')
checking for main() in -lgecodesearch... no
=> false
irb(main):007:0> ENV['DYLD_LIBRARY_PATH']='/usr/local/lib'
=> "/usr/local/lib"
irb(main):008:0> have_library('gecodesearch')
checking for main() in -lgecodesearch... no
=> false
irb(main):009:0> have_library('libgecodesearch')
checking for main() in -llibgecodesearch... no
=> false
irb(main):010:0> ENV['C_INCLUDE_PATH']='/usr/local/lib'
=> "/usr/local/lib"
irb(main):011:0> have_library('gecodesearch')
checking for main() in -lgecodesearch... no
=> false
irb(main):012:0> ENV['PATH']='/usr/local/lib'
=> "/usr/local/lib"
irb(main):013:0> have_library('gecodesearch')
checking for main() in -lgecodesearch... no
=> false 

What is the best way to solve this problem?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Adam Russell
  • 187
  • 1
  • 9
  • I've never encountered this situation directly, but I'm assuming that if you add the path to the `$LOAD_PATH` (also known as `$:`) all will work: `$: << "/usr/local/lib/mylib"` Alternatively, call the Ruby interpreter with one or more [`-I`](http://www.zenspider.com/Languages/Ruby/QuickRef.html#command-line-options) options: `ruby -I/usr/local/lib/mylib foo.rb` – Phrogz Feb 16 '12 at 17:30
  • Unfortunately, no, that is not the case `irb(main):001:0> require 'mkmf'` `=> true` `irb(main):002:0> $: << "/usr/local/lib"` `=> ["/usr/pkg/lib/ruby/site_ruby/1.9", "/usr/pkg/lib/ruby/site_ruby/1.9/i386-netbsdelf", "/usr/pkg/lib/ruby/site_ruby", "/usr/pkg/lib/ruby/vendor_ruby/1.9", "/usr/pkg/lib/ruby/vendor_ruby/1.9/i386-netbsdelf", "/usr/pkg/lib/ruby/vendor_ruby", "/usr/pkg/lib/ruby/1.9", "/usr/pkg/lib/ruby/1.9/i386-netbsdelf", "/usr/local/lib", "/usr/local/lib"]` `irb(main):003:0> have_library('gecodesearch')` checking for main() in -lgecodesearch... no => false` – Adam Russell Feb 16 '12 at 19:08
  • Don't add the root of `/usr/local/lib` to the load path, load the actual directory/directories of the libraries you need. Ruby does not search hierarchically all directories below directories in the load path. – Phrogz Feb 16 '12 at 19:09
  • The .so files are directly in /usr/local/lib, not some subdirectory. That choice was made by the lib's Makefile by default. – Adam Russell Feb 16 '12 at 19:14
  • try `nm /usr/local/lib/libgecodesearch.so` and see if it contains a _main or main symbol – jupp0r Feb 16 '12 at 19:20
  • @ jupp0r No, Using `nm` I can see that the lib in question does not have 'main' defined. `-bash-4.2$ nm libgecodesearch.so | grep -i main` returns nothing. – Adam Russell Feb 16 '12 at 19:25
  • It seems strange that have_library wants 'main' defined. Not all libs have 'main' defined..they're libs! The gem that is trying to use these library does not have any parameters specifying it should look for any other symbol though (does have_library even take such a parameter?). – Adam Russell Feb 16 '12 at 19:28
  • `mkmf` creates a little c program with a `main` function when testing for the libs. Have a look for a `mkmf.log` file. – matt Feb 16 '12 at 19:31
  • Are you on linux? I suspect you need to add `usr/local/lib` to the default linker search path. I _think_ this involves editing `/etc/ld.so.conf` and then running `ldconfig`, but I'm on a Mac so can't test it. As always, google is your friend. – matt Feb 16 '12 at 19:34
  • @matt No, I am on NetBSD. NetBSD has ldconfig also but this does not make a difference. – Adam Russell Feb 16 '12 at 21:23
  • I've been able to hack extconf.rb to generate a Makefile which I am then able to build and install the gem with. What I did was add the line `$CPPFLAGS << "-I/usr/local/include -L/usr/local/lib"` and then comment out the have_library() calls. After the Makefile is generated I can run `make` and `make install` without error. Unfortunately the resulting gem .so is unable to find the library it was linked against. :-/ I am going to manually fiddle with linker options next... – Adam Russell Feb 16 '12 at 23:03

2 Answers2

3

I've read the comments and I know you made it work, but I think I have the proper solution to the problem.

have_library checks whether it is possible to use the given library in your environment. It does so by including the library header and using one of its functions in a temporary C source file. If it is successful, then the library must be available.

have_library 'geocodesearch'
checking for main() in -lgecodesearch... no

have_library could not use the mainfunction from geocodesearch. This either means the library is not available or the function doesn't exist. In your case, it is probably the latter.

You can tell have_library which function to try by passing a second argument. For example:

have_library 'geocodesearch', 'geocodesearch_version'
checking for geocodesearch_version() in -lgecodesearch...

If you don't specify, it will simply look for the main function. You can also specify the headers to be included:

have_library 'geocodesearch', 'geocodesearch_version', %w(geocode/search.h)

In the comments, you said you solved your problem by simply eliminating the have_library calls. This is a localized solution; you will have to reapply it to any new versions of the gem.

I recommend sending a pull request to the author containing the necessary adjustments. The bug will be permanently fixed and you will also help others who might be having the same issue.

mkmf reference:

Matheus Moreira
  • 17,106
  • 3
  • 68
  • 107
1

Adding find_library first is what worked for me:

find_library('library_name', 'library_function', '/usr/local/lib')
have_library('library_name', 'library_function', 'library_header')

I ran into this while trying to build a native extension gem that depends on a custom library from /usr/local/lib under TruffleRuby 21.3.0, while the same gem works just fine under CRuby 2.7.x.

Based on the mkmf MakeMakefile docs, it seems that find_library will accept a list of search paths, and have_library will check for the header, but for some reason have_library doesn't accept search paths. But, find_library will add the path it finds to the internal search path, and then have_library will be able to find the library.

You could probably get away with just using find_library, but I'm doing both because have_library, as I understand it, can also look for the library header.


Here are the error messages I was seeing from the graalvm-native-clang -o conftest compilation command, for the sake of future searchers:

conftest.c:14:57: error: use of undeclared identifier 'library_function'
int t(void) { void ((*volatile p)()); p = (void ((*)()))library_function; return !p; }
ld.lld: error: unable to find library -llibrary_name
clang-12: error: linker command failed with exit code 1 (use -v to see invocation)
nitrogen
  • 1,559
  • 1
  • 14
  • 26