Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Building install packages with shared objects and symbolic links

I am working on a project and we are starting release binaries. We are using CMake to generate build files, and CPack to create binaries. Our binaries work, but we run into problem with shared objects. Essentially, many of the issues, arise from symbolic linking on the system, especially with shared objects with multiple links. So, let say the RPATH results from ldd (or otool) for some executable include libmpich.so.10 and I've linked /usr/lib/x86_64-linux-gnu/libmpich.so from cmake and these files are related like this:

/usr/lib/x86_64-linux-gnu/libmpich.so -> libmpich.so.10
/usr/lib/x86_64-linux-gnu/libmpich.so.10 -> libmpich.so.10.0.4
/usr/lib/x86_64-linux-gnu/libmpich.so.10.0.4

Now, for some reason the RPATH uses the intermediate link (so.10) but readlink on libmpich.so (or get_filename_component(... REALPATH)) returns libmpich.so.10.0.4. So if I install libmpich.so.10.0.4 under the name libmpich.so OR libmpich.so.10.0.4 (or create the symlink from one to the other), I've still missed the library asked for in the RPATH.

I've been playing whack-a-mole on when dealing with these and/or using a file glob to try to grab the intermediate link but I would like to do something more robust. Anyone use a good design pattern for this?

I have been looking into using functions like GET_PREREQUISITES, but those require the object to be built so I would need to add them into the install scripts somehow.... and it feels like their should be a better way.

-Jameson

P.S. I've also bee looking for a best practices guide for building binaries, either with cmake or in general. We are producing binaries on windows, linux and mac. If you know of some good links please post them as well.

like image 512
jmerkow Avatar asked Sep 29 '22 11:09

jmerkow


1 Answers

I just recently dealt with this issue myself. The cmake command get_filename_component(... REALPATH) resovles ALL levels of a symlink in one call.

To resolve just a single level of a symlink you can call 'readlink' directly from cmake, since it's available on every symlink-enabled platform that you're likely to build on (Linux, Mac OS X, and *BSD).

So, if you want to reproduce the complete chain of symbolic links, you'd code up something like this in your cmake script:

#If given the following library path:
set(lib  "/usr/lib/x86_64-linux-gnu/libmpich.so")

#Make sure the initial path is absolute.
get_filename_component(lib "${lib}" ABSOLUTE)

#Store initial path as first element in list.
set(symlist "${lib}")

while(UNIX AND IS_SYMLINK "${lib}")
  #Grab path to directory containing the current symlink.
  get_filename_component(sym_path "${lib}" DIRECTORY)

  #Resolve one level of symlink, store resolved path back in lib.
  execute_process(COMMAND readlink "${lib}"
                  RESULT_VARIABLE errMsg
                  OUTPUT_VARIABLE lib
                  OUTPUT_STRIP_TRAILING_WHITESPACE)

  #Check to make sure readlink executed correctly.
  if(errMsg AND (NOT "${errMsg}" EQUAL "0"))
    message(FATAL_ERROR "Error calling readlink on library.")
  endif()

  #Convert resolved path to an absolute path, if it isn't one already.
  if(NOT IS_ABSOLUTE "${lib}")
    set(lib "${sym_path}/${lib}")
  endif()

  #Append resolved path to symlink resolution list.
  list(APPEND symlist "${lib}")
endwhile()

#Now symlist will contain the following:
# [...]/libmpich.so;[...]/libmpich.so.10;[...]/libmpich.so.10.0.4
like image 140
swirley161 Avatar answered Oct 14 '22 00:10

swirley161