Conan: Consider NOT doing set(CMAKE_SKIP_RPATH 1) on OSX

Created on 19 Jun 2016  路  19Comments  路  Source: conan-io/conan

I've just spent a few hours modifying the SFML package to get it working on OSX, see http://github.com/cpbotha/conan-sfml

My last hurdle was that my sfml test binary refused to see the linked libsfml* dylibs, even when I followed the conan documentation and used imports to have the libs copied into project dir (I also had to modify my cmake to copy those libs together with the generated binary). It turned out that the generated binary searched for for example @rpath/libsfmlaudio.2.3.dylib... @rpath means that dyld will search for that shared library in all directories specified via -rpath to the linked and hence embedded in the binary. Now that sounds like a nice solution!

By default, cmake will add -L directories to the -rpath, but unfortunately, the conan-generated cmake settings set CMAKE_SKIP_RPATH, so none of this info is embedded.

When I comment that out, the test binary finds all of the SFML dylibs, and they find the sfml-included frameworks, all with no problems!

I think that this is a much cleaner dev-env solution than copying the libraries into the binary directory, as suggested by the docs. Now dyld will be able to find any of the conan package libraries that are linked, without any copying.

Here's more relevant info about cmake's handling of @rpath on the Mac: https://cmake.org/Wiki/CMake_RPATH_handling#Mac_OS_X_and_the_RPATH

Most helpful comment

I just want to comment that I fall in this same situation recently when using conan_basic_setup inside the CMakeLists.txt of the test_package folder of my recipes.

I think it is wrong to set CMAKE_SKIP_RPATH there. Each project will handle the RPATHs differently depending on the kind of targets that need to be handled (static libs, shared libs, applications).

For example, for every recipe that I write I always write a test_package to check that the libraries that I am generating have the RPATHs correctly configured. In that test_package I import the shared libraries into a lib folder outside of the bin folder. Then in test_package/CMakeLists.txt I add this:

    if (APPLE)
        set(CMAKE_INSTALL_RPATH "@executable_path/../lib")
    else()
        set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
    endif()

Because of this issue I ended up doing this:

if (UNIX)
    conan_set_find_paths()
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
    set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
    if (APPLE)
        set(CMAKE_INSTALL_RPATH "@executable_path/../lib")
    else()
        set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
    endif()
else()
    conan_basic_setup()
endif()

In order to avoid the internal call in conan_basic_setup to the RPATH setup. But I want to still call conan_basic_setup on Windows because of different reasons.

All 19 comments

That would be a good improvement. The thing is that we didn't manage to make it work in a consistent way, but with the CMAKE_SKIP_RPATH. Consider that in your case it might work because you are building it locally, and it keeps the rpath to your local build. But will such binary work when another user retrieve it? The rpath will be pointing to your machine local path, and will not work for them.

You might be able to check this by uploading the package to conan.io and then consuming it again, but setting the CONAN_USER_HOME environment variable (check the docs http://docs.conan.io/en/latest/howtos/custom_cache.html) to a different directory. Please tell me if this makes sense, or if it works. Certainly @lasote might be interested in this, too.

Thanks very much for all your work!

From the documentation, it looks like the intention of @rpath on OSX is that you can specify a link dependency as for example @rpath/yourlib.dylib. The dynamic linker on OSX will substitute @rpath with each of the rpaths embedded in the main application binary until it finds yourlib.dylib. This gives the conan-using developer an easy-to-use default (in any case easier to use than copying libraries around everywhere) as well as more control over which libraries their app dynamically links with.

I'll export my package to conan.io, then destroy my own ~/.conan and consume the published package and report back.

Right, I exported the package to https://www.conan.io/source/sfml/2.3.2/cpbotha/stable after which I completely nuked my ~/.conan and started afresh with sfml test app's conanfile. If I keep the default CMAKE_SKIP_RPATH 1 this happens:

/Users/cpbotha/Library/Caches/CLion2016.2/cmake/generated/conan-sfml-test-76bb9f1e/76bb9f1e/Debug/bin/sfmltest
dyld: Library not loaded: @rpath/libsfml-audio.2.3.dylib
  Referenced from: /Users/cpbotha/Library/Caches/CLion2016.2/cmake/generated/conan-sfml-test-76bb9f1e/76bb9f1e/Debug/bin/sfmltest
  Reason: image not found

Note that it's looking for @rpath/libsfml-audio.2.3.dylib -- Ideally I would want that @rpath to be substituted by conan with the path of the relevant SFML package installlation.

Once I comment out the CMAKE_SKIP_RPATH in conanbuildinfo.cmake. this app runs like a charm!

When I look at my sfmltest app's rpaths I see the following:

$ otool -l sfmltest | grep LC_RPATH -A2
          cmd LC_RPATH
      cmdsize 120
         path /Users/cpbotha/.conan/data/sfml/2.3.2/cpbotha/stable/package/0b89d7271c641c0ed63cb474727fe5887401d9c0/lib (offset 12)

With the actual library dependencies as follows:

$ otool -L sfmltest 
sfmltest:
        @rpath/libsfml-audio.2.3.dylib (compatibility version 2.3.0, current version 2.3.2)
        @rpath/libsfml-graphics.2.3.dylib (compatibility version 2.3.0, current version 2.3.2)
        @rpath/libsfml-network.2.3.dylib (compatibility version 2.3.0, current version 2.3.2)
        @rpath/libsfml-window.2.3.dylib (compatibility version 2.3.0, current version 2.3.2)
        @rpath/libsfml-system.2.3.dylib (compatibility version 2.3.0, current version 2.3.2)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)
        /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)

Pretty neat right? The @rpath is replaced by dyld, and it successfully finds the right one. If I were to change the conan dependency, it would neatly find the alternative version of these libraries.

Hhmm, interesting. Indeed, replacing the @rpath would be probably the best approach, though might be complicated, have to check.

My main concern is that you are reusing exactly the same path. Even if you wipe out your .conan folder completely, when you regenerate things, the path is still the same:

/Users/cpbotha/.conan/data/sfml/2.3.2/cpbotha/stable/package/0b89d7271c641c0ed63cb474727fe5887401d9c0/lib

But when another user consumes it, the path will be different, and this causes problems. That is why I suggested to user CONAN_USER_HOME as it allows to setup a different folder other than /Users/cpbotha.

Probably my concerns are irrelevant, this seems very interesting and promising. We'll deepen into this asap (will have to wait until Tuesday), together with @lasote, let's see what can be done by conan. Thanks very much again!

Oh right, I just redid conan install with CONAN_USER_HOME=/tmp:

$ otool -l sfmltest | grep LC_RPATH -A2
          cmd LC_RPATH
      cmdsize 112
         path /tmp/.conan/data/sfml/2.3.2/cpbotha/stable/package/0b89d7271c641c0ed63cb474727fe5887401d9c0/lib (offset 12)

... and binary still runs like dream (but only with CMAKE_SKIP_RPATH setting commented out)

As far as I can see, cmake ensures that the -L arguments also get installed as rpath arguments in the application binary.

Thanks for handling me today. ;)

I will try it and see if it has no hidden problems. The problem with rpaths we found in OSx is that if the path specified in a rpath (of a dylib or executable) don't exist, it fails without looking other paths. So, imagine you upload sfml lib with a harcoded rpath and I download it and it doesn't exist in my Mac... what we saw is that fails, so we started to tell cmake not to keep rpaths.

But maybe we miss something. I will try it.

thanks!

Oof, yes that would certainly be uncomfortable.

Let me know if you need help testing this. It sounds a bit extreme that dyld gives up at the first non-existent rpath I have to say.

In any case, it also sounds like something that the OSX packager / library writers should perhaps take care of in a better way so that the users of their libraries are able to use the rpath mechanism like it was intended.

Hi, I've been playing a little with your modified conanfile.py and I have some advances about the dylib/rpath mistery.

I saw that the problem is not that conan sets CMAKE_SKIP_RPATH in your executable build, the problem is that @memsharded is invoking the sfml cmake without including the conanbuildinfo.cmake, so the CMAKE_SKIP_RPATH is not called creating the library. The problem is that the dylibs are created with this @rpath/blabla (Bad created). So, in my fork (linked below) you can see how I "inject" or "paste" some lines to the original CMakeLists.txt from sfml.

But, the problems don't end here. Now in my fork, it fails with a similar problem with the OSx frameworks dylibs. If you see the firsts lines of the sfml compilation:

-- Found OpenGL: /System/Library/Frameworks/OpenGL.framework  
-- Found JPEG: /Users/usuario/.conan/data/sfml/2.3.2/memsharded/testing/build/0b89d7271c641c0ed63cb474727fe5887401d9c0/SFML-2.3.2/extlibs/libs-osx/lib/libjpeg.a  
-- Found OpenAL: /Users/usuario/.conan/data/sfml/2.3.2/memsharded/testing/build/0b89d7271c641c0ed63cb474727fe5887401d9c0/SFML-2.3.2/extlibs/libs-osx/Frameworks/OpenAL.framework  
-- Found VORBIS: /Users/usuario/.conan/data/sfml/2.3.2/memsharded/testing/build/0b89d7271c641c0ed63cb474727fe5887401d9c0/SFML-2.3.2/extlibs/libs-osx/Frameworks/vorbis.framework  
-- Found FLAC: /Users/usuario/.conan/data/sfml/2.3.2/memsharded/testing/build/0b89d7271c641c0ed63cb474727fe5887401d9c0/SFML-2.3.2/extlibs/libs-osx/Frameworks/FLAC.framework  

The real problem is that all those dependencies should be conan packages too to make the things right. Of course we could link agains the Frameworks (investigating how the hell is using the rpath), but I think the sfml conan package is in a VERY early stage and have some work to do unless it works fine.

You can see my fork here (not working at all).
https://github.com/lasote/conan-sfml/blob/master/conanfile.py

I don't understand why you write that @rpath/blabla is "Bad created"?

Having dylibs with @rpath/blabla could be quite advantageous. This means that the consuming executable / app gets to determine in which directories blabla is searched for, which is great, because we don't have to copy libraries around. Also, the existing default behaviour can be emulated by linking the executable with -rpath .

Did you see that everything works 100% if you comment out CMAKE_SKIP_RPATH in the executable build? CMAKE implants the correct -rpath settings based on -L, sort of emulating default behaviour under Linux.

I agree with you that ideally SFML will have to link to the conan versions of those dependencies. However, completely disabling @rpath support, which works well when used correctly, is not really gaining you very much. Furthermore, to get more devs to use conan, it makes sense to make it as easy as possible to use. (I have extensive experience using and writing packages for both pip and npm, both for work and play. I would like for conan to be that easy to use, although I understand that the problems are different.)

Sorry I meant "Bad created" as "not the expected by @memsharded nor me" because in our experience, it breaks the package reusing.

Could you override the variable CMAKE_SKIP_RPATH after include the conanbuildinfo.cmake in your package instead of comment it? Does it work? If true, at least we have a clean way to solve this in the meanwhile.

I will try to get time to investigate how CMake and the rpath are working.

I did this in one of my packages as well:

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
set(CMAKE_SKIP_RPATH OFF)

And it worked just fine for me.

@tru is a public package that I could take a look?

@cpbotha could you try to change the SET(CMAKE_SKIP_RPATH 1) with a SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) and check if it works as expected?

I've been playing with it, and CMake keeps the bad absolute paths for build locations, and then, when cmake installs libraries it cleans their rpaths. The problem was by default the build rpaths (we usually don't install targets in conan packages). With CMAKE_BUILD_WITH_INSTALL_RPATH it seems that it keeps clean the build rpaths too and our test are passing.

Would be nice if you can try it with sfml.

Thanks!

Are you building your own binaries with cmake? Cmake 'make' and 'make install' set the rpaths differently. 'make' alone doesn't necessarily produce binaries that are relocatable, since it adds the the paths to all linked libraries to the rpath.

However, the dynamic loader will only find them there if the "install name" of those dylibs starts with @rpath. Otherwise it will only look in the directories pointed out by the DYLD_LIBRARY_PATH environment variable.

By the looks of it, the libraries you are linking against have the "@rpath" portion embedded in their install name, which believe it or not, it's a good thing. E.g., if your binary links against a library with install name libAAA.dylib (as opposed to @rpath/libAAA.dylib, and cmake sets the rpath to the path of the location of libAAA.dylib (e.g., the directory inside .conan), the dynamic loader will NEVER look for that library in that location, and at runtime, if it finds it in /usr/local/lib, or /usr/lib, that's the one it will choose, which may cause mayhem since it could be a different version altogether)

My preferred approach based on your message:
You mention that you are using conan importsto copy the libs to the binary dir. Are you copying it alongside the binary, or in a ./lib directory? Either way, the best approach would be to set the rpath of the binary you are creating to be relative to the executable path.

E.g., if you are copying all the necessary dylibs in ./lib (relative to the executable), set the rpath to: @executable_path/lib

You can achieve this with cmake:
SET(CMAKE_INSTALL_RPATH "@executable_path/lib")

If you are also building and linking against your own shared libraries in your cmake project, make sure they are being created with "@rpath" prepended to their install name:
set (MACOSX_RPATH ON)

Note: this will work for the binaries as produced by "make install". You will need to call the install command in cmake for your executable, and set the paths there relative to CMAKE_INSTALL_PREFIX.

@jcar87 thank you very very much for your comments. Very useful. I'll give some more time to this and search for the best approach.

I finally started uploading some of my packages to the public repo. I have a working example of an rpath-enabled library and a working example of builds using that library from different build tools.

https://github.com/kent-at-multiscale/conan-zlib

For every library I build, I have to manually edit their build process to fix their use of rpath. However, consumers of the libraries do not need to do much. The only tricky part is when a library includes a tool that links to a dependency library, and the package uses that tool as part of its own build. Google's Protobuf is an example, it builds protoc and then uses it to generate code.

My solution was to use the "import" functionality. By importing both the bin and lib dirs from dependencies into the consuming package, all of the binaries can use a standard rpath of $ORIGIN/../lib or @executable_path/../lib;@loader_path/ and the binaries in the bin dir will just work. (For the Protobuf build itself, I let it kinda hack around the issue.)

I will keep working on getting more rpath-enabled packages uploaded to demonstrate my solution.

I just want to comment that I fall in this same situation recently when using conan_basic_setup inside the CMakeLists.txt of the test_package folder of my recipes.

I think it is wrong to set CMAKE_SKIP_RPATH there. Each project will handle the RPATHs differently depending on the kind of targets that need to be handled (static libs, shared libs, applications).

For example, for every recipe that I write I always write a test_package to check that the libraries that I am generating have the RPATHs correctly configured. In that test_package I import the shared libraries into a lib folder outside of the bin folder. Then in test_package/CMakeLists.txt I add this:

    if (APPLE)
        set(CMAKE_INSTALL_RPATH "@executable_path/../lib")
    else()
        set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
    endif()

Because of this issue I ended up doing this:

if (UNIX)
    conan_set_find_paths()
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
    set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
    if (APPLE)
        set(CMAKE_INSTALL_RPATH "@executable_path/../lib")
    else()
        set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
    endif()
else()
    conan_basic_setup()
endif()

In order to avoid the internal call in conan_basic_setup to the RPATH setup. But I want to still call conan_basic_setup on Windows because of different reasons.

Thanks @piponazo We will review all the rpaths stuff for 0.26 and will take into consideration again the CMAKE_SKIP_RPATH. Probably it makes sense to not default it or in the worst case provide a better way to skip the automatic assignment (I'm afraid of break builds). Anyway, we have to analyze it carefully, and document the different approaches and how to achieve it.
It's related to #1303 #1238 #776

SKIP_RPATH argument to conan_basic_setup() has been released in 0.26

Was this page helpful?
0 / 5 - 0 ratings