Issues with Setting the RPATH

I’m really struggling with the RPATH issues on macOS. I think I read quite some blogposts, etc but I still cannot get it right, and I hope someone could help me a bit. So, here is the problem:

I’m trying to build an executable, Engine, which is linked to a few static libraries and some system libraries as follow.

  • Common is a STATIC library linked to BOOST, libJSON and libArchive.
  • R-Interface is a STATIC library linked to Common, libR, libRcpp, and libRInside
  • and finally the Engine is a STATIC library linked to Common, and R-Interface.

I pull some of those packages via the find_package and some via the pkg_check_modules (e.g., libR) and link them together. So, for R-Interface I have:

target_link_libraries(
	R-Interface PUBLIC
	Common
	
	PkgConfig::LIBR

	${_LIB_RCPP}
	${_LIB_RInside}
)

and ${_LIB_RCPP} and ${_LIB_RInside} are set by find_library as follow, so that they point to my system Rcpp.so and RInside.so.

find_library(_LIB_RCPP
	NAMES Rcpp.so
	PATHS ${_Rcpp_HOME}/libs
	NO_CACHE
	REQUIRED)

find_library(_LIB_RInside
	NAMES RInside.so
	PATHS ${_RInside_HOME}/libs
	NO_CACHE
	REQUIRED)

Now, the problem is when I build everything and run the Engine, I see that the Engine cannot find the Rcpp.so,

dyld[41231]: Library not loaded: Rcpp.so
  Referenced from: build/Engine
  Reason: tried: 'Rcpp.so' (no such file), '/usr/local/lib/Rcpp.so' (no such file), '/usr/lib/Rcpp.so' (no such file), '/Users/amabdol/Projects/JASP/jasp-desktop/build/Rcpp.so' (no such file), '/usr/local/lib/Rcpp.so' (no such file), '/usr/lib/Rcpp.so' (no such file)

I started reading and I realized that I should add the RPATH on macOS and I added these in an attempt to add the RPATH to the binary.

set(CMAKE_SKIP_BUILD_RPATH OFF)
set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)

set(CMAKE_INSTALL_RPATH "/opt/homebrew/lib/R/4.1/site-library/Rcpp/libs/;/opt/homebrew/lib/R/4.1/site-library/RInside/libs/")
set(CMAKE_BUILD_RPATH "/opt/homebrew/lib/R/4.1/site-library/Rcpp/libs/;/opt/homebrew/lib/R/4.1/site-library/RInside/libs/")

However, I still get the error, and I don’t know why, especially because I see that LC_RPATH is set both in otool -l Engine and otool -l R-Interface.

libR-Interface.dylib and Engine:

Load command 27
          cmd LC_RPATH
      cmdsize 64
         path /usr/opt/homebrew/lib/R/4.1/site-library/Rcpp/libs/ (offset 12)
Load command 28
          cmd LC_RPATH
      cmdsize 64
         path /usr/opt/homebrew/lib/R/4.1/site-library/Rcpp/ (offset 12)

however, the otool -L R-Interface still looks off, right?

R-Interface/libR-Interface.dylib:
	@rpath/libR-Interface.dylib (compatibility version 0.0.0, current version 0.0.0)
	/opt/homebrew/opt/boost/lib/libboost_nowide-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
	/opt/homebrew/opt/boost/lib/libboost_filesystem-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
	/opt/homebrew/opt/boost/lib/libboost_system-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
	/opt/homebrew/opt/boost/lib/libboost_date_time-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
	/opt/homebrew/opt/boost/lib/libboost_timer-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
	/opt/homebrew/opt/boost/lib/libboost_chrono-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
	Rcpp.so (compatibility version 0.0.0, current version 0.0.0)
	RInside.so (compatibility version 0.0.0, current version 0.0.0)
	/opt/homebrew/opt/libarchive/lib/libarchive.13.dylib (compatibility version 19.0.0, current version 19.2.0)
	/opt/homebrew/opt/jsoncpp/lib/libjsoncpp.24.dylib (compatibility version 24.0.0, current version 24.0.0)
	/opt/homebrew/opt/r/lib/R/lib/libR.dylib (compatibility version 4.1.0, current version 4.1.1)
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1200.3.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1311.0.0)

I am not really sure what I am missing at this point, so, any help will be appreciated! :pray:t3:

Rcpp.so comes from the library ID of what is linked. If it does not use @rpath/ as a prefix, LC_RPATH has no effect. Likewise, if @rpath/ is used, an LC_RPATH entry must be used.

You’ll need to recompile Rcpp.so and RInside.so to use @rpath/ as their library id using CMAKE_INSTALL_NAME_DIR or whatever facilities its buildsystem provides. Alternatively, you can modify them using install_name_dir, but do not do this during the build of your project; it should be part of the installation of those projects. Trying to “fix up” external dependencies in your own build like this is a never-ending battle.

That’s exactly what I would like to avoid. We have a hack-y QMake setup that works exactly like you described. But, it’s really not sustainable, and we want to change it. Our problem is that we want to ship the R.framework with our software but we have to deal with RPATH issues every time, and later of course signing and verification.

I’m not sure if it’s possible, but if it is, is it a good idea to see if I can compile the entire R.framework before my target and link it to my project?

If R’s install doesn’t do it, you can add additional fixup things after its install to your build scripts. Note that in this case, you’ll also need to update the library references between each, not just the library ids.

Thanks for your replies! :slight_smile:

I think R.framework installer actually do it, if I read this right, R Installation and Administration. So, if I do this to the right path, hopefully those libraries (installed using the correctly build R) will be linked correctly, and I can link them to my executable.

Another thing, I see that CMake can link to a *.framework, and I’m wondering if this is something that I can use here? :thinking:

I think I got around this by building the Framework from source and making sure that it sets its RPATH correctly. I could also link the framework directory to my target!