RPATH, Mac OS X, and how to load a library from anywhere and execute macros from anywhere

I’ve been struggling with this for quite some time now and I can’t find a solution. Searching for answers, I came across a lot of things about rpath, the way to adapt the CMakeLists.txt file to make it work, etc. It seems that my problem is related to the fact Mac OS do not allow the use of LD_LIBRARY_PATH anymore. And I don’t want to bypass default system settings as it’s often suggested. Looking for solutions on the forums, not only on ROOT forums, I found out that some new instructions need to be added in the CMakeLists.txt file, so I added this:

if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
  message(STATUS "Darwin specific RPATH configuration")
  set(CMAKE_MACOSX_RPATH TRUE)
  set(CMAKE_SKIP_BUILD_RPATH FALSE)
  set(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE)
  list(APPEND CMAKE_BUILD_RPATH ${PROJECT_BINARY_DIR}/lib)
endif()

The output of otool utility can be found here. It seems reasonable to me, but I’m not familiar with rpath.

The only way I can make everything work is to change directory to the one where the macro file is located. Then, I load the library using the relative path, and I run my script. It looks like this:

.L ../lib/libMyLib.so
.L MyMacro.C
test_func()

Ok it works like this, but I’d like to be able loading the library from anywhere, and execute the macro from anywhere as well. For example, if I try this, so from the parent directory:

.L ./lib/libMyLib.so
.L ./scripts/MyMacro.C

and then I have this message:

libMyClasses dictionary payload:5:10: remark: could not acquire lock file for module MyClasses': failed to create unique file
      /usr/local/Cellar/root/6.24.06_2/lib/root/MyClasses.pcm.lock-ae721b5d: Permission denied [-Rmodule-build]
#include "/<path to>/inc/Data.h"
         ^
libMyClasses dictionary payload:5:10: remark: building module 'MyClasses' as '/usr/local/Cellar/root/6.24.06_2/lib/root/MyClasses.pcm' [-Rmodule-build]
error: unable to open output file '/usr/local/Cellar/root/6.24.06_2/lib/root/MyClasses.pcm': 'Permission denied'
libLIVlihoodClasses dictionary payload:5:10: remark: finished building module 'MyClasses' [-Rmodule-build]
libLIVlihoodClasses dictionary payload:5:10: fatal error: could not build module 'MyClasses'

It’s totally normal that there is no permission to write in “/usr/local/Cellar/root/6.24.06_2/lib/root/”, but why root tries to write there at the first place? How can I tell root to write somewhere else?

Not being an expert with cmake or rpath, everything is quite difficult to understand!

Thanks for the help,
Cheers!

ROOT Version: 6.24/06, installed using brew
Platform: Mac OS 11.6.5
Compiler: Apple clang version 13.0.0 (clang-1300.0.29.3)


Hi @Julien_Bolmont_LPNHE ,
and sorry for the high latency.

If I understand correctly, given ROOT installed via homebrew this works inside the ROOT prompt:

.L ../lib/libMyLib.so
.L MyMacro.C
test_func()

but this does not:

.L ./lib/libMyLib.so
.L ./scripts/MyMacro.C

as the dictionary generation then tries to write to /usr/local/Cellar/... and obviously fails.
And I guess the CMakeLists.txt is how you build libMyLib.so?

Regarding the particular error: I am not sure why .L ./scripts/MyMacro.C would try to write to /usr/local/Cellar/root/6.24.06_2/lib/root but I think at least one similar problem was fixed in v6.26, could you maybe try with v6.26.02?

Cheers,
Enrico

Hi @eguiraud ,
Thanks for your answer.
Yes, the CMakeLists.txt is how I build libMylib.so.
As you suggested, I switched to root 6.26/02, installed using homebrew.
I have the exact same error message.

The build output files are created in the lib directory:

bolmont@host Mylib-build % ls lib
libMylib.so			libMylibClasses.rootmap	libMylibClasses_rdict.pcm

Here is the output:

root [0] .L ./lib/libMylib.so 

RooFit v3.60 -- Developed by Wouter Verkerke and David Kirkby 
                Copyright (C) 2000-2013 NIKHEF, University of California & Stanford University
                All rights reserved, please read http://roofit.sourceforge.net/license.txt

root [1] .L ./scripts/MyMacro.C 
libMyClasses dictionary payload:5:10: remark: could not acquire lock file for module 'libMyClasses': failed to create unique file /usr/local/Cellar/root/6.26.02/lib/root/libMyClasses.pcm.lock-b895f331: Permission denied [-Rmodule-build]
#include "/<path to>/inc/Data.h"
         ^
libMyClasses dictionary payload:5:10: remark: building module 'libMyClasses' as '/usr/local/Cellar/root/6.26.02/lib/root/libMyClasses.pcm' [-Rmodule-build]
error: unable to open output file '/usr/local/Cellar/root/6.26.02/lib/libMyClasses.pcm': 'Permission denied'
libMyClasses dictionary payload:5:10: remark: finished building module 'libMyClasses' [-Rmodule-build]
libMyClasses dictionary payload:5:10: fatal error: could not build module 'libMyClasses'
...

Cheers,
Julien

Thank you for the clarifications. @vvassilev , @Axel , any idea why .L ./scripts/MyMacro.C would try to write in ROOT’s install dir (/usr/local/Cellar/root/6.26.02/lib/root/libMyClasses.pcm.lock-b895f331)?

Hi!
It seems there is no simple solution to this. Maybe I have to find another way to load the library and execute macros…

How was libMyLib.so generated and in particular what was the rootcling command line?

I realize now that maybe I do something wrong. rootmap and pcm files are generated in the “build” directory and I move them at the end in the “build/lib” directory.

Nonetheless, here is a copy of my CMakeLists.txt file.

# CMakeLists.txt for LIVelihood software. It creates a library with dictionary.
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(LIV)


message(STATUS "System name: ${CMAKE_SYSTEM_NAME}")
message(STATUS "System version: ${CMAKE_SYSTEM_VERSION}")
message(STATUS "Processor: ${CMAKE_SYSTEM_PROCESSOR}")

if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
  message(STATUS "Darwin specific RPATH configuration")
  set(CMAKE_MACOSX_RPATH TRUE)
  set(CMAKE_SKIP_BUILD_RPATH FALSE)
  set(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE)
  list(APPEND CMAKE_BUILD_RPATH ${PROJECT_BINARY_DIR}/lib)
endif()

# Check if $ROOTSYS is set
if(DEFINED ENV{ROOTSYS})
  message(STATUS "ROOTSYS found")
  list(APPEND CMAKE_PREFIX_PATH $ENV{ROOTSYS})
else()
# If $ROOTSYS is not set, try anyway to find the file ROOTConfig.cmake.
# We do this using the root-config utility.
  message(STATUS "ROOTSYS not found, trying another way")
  execute_process(
    COMMAND root-config --prefix
    OUTPUT_VARIABLE ROOT_base_dir
    RESULT_VARIABLE err
  )

  if(NOT ${err} EQUAL 0)
    message(FATAL_ERROR "Please check root 6 is properly installed!")
  endif()

  # The output string ends with a '\n', so we remove it.
  string(REPLACE "\n" "" ROOT_base_dir_strip ${ROOT_base_dir})

  if(EXISTS ${ROOT_base_dir_strip}/share/root/cmake/ROOTConfig.cmake)
    message(STATUS "ROOTConfig.cmake found")
    list(APPEND CMAKE_PREFIX_PATH ${ROOT_base_dir_strip})
  else()
    message(FATAL_ERROR "ROOTConfig.cmake not found in: ${ROOT_base_dir_strip}")
  endif()
endif()

# This is to check if cfitsio library is installed.
# For the moment, this is ignored and only a warning is issued.
find_library(CFITSIO_LIB cfitsio)
if(NOT CFITSIO_LIB)
  message(WARNING "cfitsio library not found. DL3 interface won't be available!")
  set(CFITSIO_LIB "")
else()
  message(STATUS "cfitsio library found")
endif()


# You need to tell CMake where to find the ROOT installation. This can be done in a number of ways:
#   - ROOT built with classic configure/make use the provided $ROOTSYS/etc/cmake/FindROOT.cmake
#   - ROOT built with CMake. Add in CMAKE_PREFIX_PATH the installation prefix for ROOT

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_SHARED_LIBRARY_SUFFIX ".so")

#---Locate the ROOT 6 package and define a number of variables (e.g. ROOT_INCLUDE_DIRS)
find_package(ROOT 6 REQUIRED COMPONENTS RIO Net RooFit RooFitCore)
include(${ROOT_USE_FILE})
link_directories(${ROOT_LIBRARY_DIR})


#---Copy the scripts in the build directory
if(NOT EXISTS ${PROJECT_BINARY_DIR}/scripts)
  message(STATUS "Copying the directory scripts in build directory")
  file(COPY ${PROJECT_SOURCE_DIR}/scripts DESTINATION ${PROJECT_BINARY_DIR}/)
endif()

#---The library will be placed in the lib directory
if(NOT EXISTS ${PROJECT_BINARY_DIR}/lib)
  message(STATUS "Creating the directory lib in build directory")
  file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
endif()

include_directories("${PROJECT_SOURCE_DIR}/inc")

#---Note that LinkDef file must have extension .hh so that it's not included in variable ${header_files}.
#---Note also that because of the use of GLOB here with a regular expression, it is necessary to rebuild
#---everything from scratch in the case a new source or header file is added.
file(GLOB source_files ${PROJECT_SOURCE_DIR}/src/*.cpp)
file(GLOB header_files ${PROJECT_SOURCE_DIR}/inc/*.h)


#---All files generated here are created in the build directory. they are moved at the end of the build.
ROOT_GENERATE_DICTIONARY(
  LIVlihoodClassesDict ${header_files}
  LINKDEF ${PROJECT_SOURCE_DIR}/inc/LinkDef.hh
  MODULE LIVlihoodClasses)


list(APPEND source_files ${PROJECT_BINARY_DIR}/LIVlihoodClassesDict.cxx)


#---Create a shared library with generated dictionary
add_library(LIV SHARED ${source_files})

#target_compile_options(LIV PRIVATE -Wall -Wextra -Wpedantic -Werror)
target_compile_options(LIV PRIVATE -Wall -Wextra)


target_link_libraries(LIV ${ROOT_LIBRARIES} ${CFITSIO_LIB})
set_target_properties(LIV PROPERTIES
  LINKER_LANGUAGE CXX
  LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)


#---POST BUILD actions: move rootmap and pcm files, delete dictionary cxx file.
add_custom_command(TARGET LIV POST_BUILD
  COMMAND mv ${PROJECT_BINARY_DIR}/libLIVlihoodClasses.rootmap ${PROJECT_BINARY_DIR}/lib/.
  VERBATIM)

add_custom_command(TARGET LIV POST_BUILD
  COMMAND mv ${PROJECT_BINARY_DIR}/libLIVlihoodClasses_rdict.pcm ${PROJECT_BINARY_DIR}/lib/.
  VERBATIM)

add_custom_command(TARGET LIV POST_BUILD
  COMMAND rm -f ${PROJECT_BINARY_DIR}/LIVlihoodClassesDict.cxx VERBATIM)

Hi! No clue? Thanks anyway!

How was libMyLib.so generated and in particular what was the rootcling command line?

can you reply to this question? And in addition attach the output of the relevant rootcling command with -v4 as a invocation parameter?

Hi @bolmont, in the attached CMakeLists I do not see rules for the creation of libMylib.so. Can you attach the other cmake file if any?

Hi,

Using “make --just-print”, I was able to find the rootling invocation. Then, I run it with option -v4.
You’ll find the output of it here: file

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.