I am using CMake to build a library. Here is my CMakeLists.txt:
cmake_minimum_required(VERSION 3.11)
project(simpleLibrary)
set(CMAKE_CXX_STANDARD 17)
add_library(simpleLibrary SHARED main.cpp)
It is very simple and clear. It builds a library in cmake-build-debug. I use otool to check shared library id:
➜ cmake-build-debug git:(master) ✗ otool -L libsimpleLibrary.dylib
libsimpleLibrary.dylib:
#rpath/libsimpleLibrary.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 904.4.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1)
➜ cmake-build-debug git:(master) ✗
It builds with #rpath by default. In my case I need to use #executable_path because I am facing linkage problems in the Mac OS without Xcode installed. Is there a way to change it in CMake build? I could change it using install_name_tool but I have to make that on build stage. Thanks in advance.
Related
I am trying to do a C++ project using CMake, Conan and Qt6.
At first, I was able to add Qt Core in the project (hello world at the time) and call qDebug() instead of std::cout but then I added a lib with Conan (libgphoto2) and now I can't use Qt in the project.
The build does not fail, but running the app gives this error :
dyld[68458]: Library not loaded: '#rpath/QtCore.framework/Versions/A/QtCore'
Referenced from: '/Users/chell/code/skytracker/cmake-build-debug/bin/skytracker'
Reason: tried: '/Library/Frameworks/QtCore.framework/Versions/A/QtCore' (no such file), '/System/Library/Frameworks/QtCore.framework/Versions/A/QtCore' (no such file)
If I understand correctly what I read on internet when trying to find a solution, is that at runtime, the linker will replace #rpath with a list of path to look for the lib. Here, it fails because it doesn't have ~/Qt/6.4.2/macos/lib where the QtCore.framework folder is ?
I'm not using Qt Creator at all, I'm using CLion, I'll just need some Qt libs.
Here's the CMakeLists.txt :
cmake_minimum_required(VERSION 3.22)
project(skytracker)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#Conan integration
if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/0.18.1/conan.cmake"
"${CMAKE_BINARY_DIR}/conan.cmake"
TLS_VERIFY ON)
endif()
include(${CMAKE_BINARY_DIR}/conan.cmake)
set(CI false CACHE BOOL "Set true if CI build")
if(${CI})
message("Using CI profile")
set(CMAKE_C_COMPILER /usr/bin/gcc)
set(CMAKE_CXX_COMPILER /usr/bin/g++)
set(CONANPROFILE ../CI.conanprofile)
endif()
conan_cmake_install(PATH_OR_REFERENCE ..
BUILD missing
REMOTE conancenter
PROFILE ${CONANPROFILE})
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup(TARGETS)
set(sources src/cpp/main.cpp
src/cpp/services/PhotoService.cpp src/cpp/services/PhotoService.h
# src/cpp/entities/Camera.h src/cpp/entities/Camera.cpp
# src/cpp/entities/NikonCamera.cpp src/cpp/entities/NikonCamera.h
)
set(CMAKE_AUTOMOC ON) #Qt C++ extensions
set(CMAKE_AUTOUIC OFF) #Enable GUI compile
set(CMAKE_AUTORCC OFF) #Enable QRC files
if(APPLE)
set(Qt6_DIR ~/Qt/6.4.2/macos/lib/cmake/Qt6/)
set(CONANPROFILE macos.conanprofile)
set(CMAKE_MACOSX_RPATH 1)
elseif(UNIX)
set(Qt6_DIR ~/Qt/6.4.2/gcc_64/lib/cmake/Qt6/)
set(CONANPROFILE unix.conanprofile)
elseif(WIN32)
set(Qt6_DIR C:/Qt/6.4.2/mingw_64/lib/cmake/Qt6/)
set(CONANPROFILE windows.conanprofile)
endif()
#find the Qt libs used by the project
find_package(Qt6 6.4.2 REQUIRED COMPONENTS Core)
include(${CMAKE_BINARY_DIR}/conan_paths.cmake)
# give the compiler the path to the libs
link_directories("${CONAN_LIB_DIRS}")
# and to the headers
include_directories("${CONAN_INCLUDE_DIRS}")
add_executable(skytracker ${sources})
#link all Qt libs to the executable
target_link_libraries(skytracker PRIVATE Qt6::Core)
target_link_libraries(skytracker PRIVATE ${CONAN_LIBS})
Running otool -L skytracker gives me this :
skytracker:
#rpath/QtCore.framework/Versions/A/QtCore (compatibility version 6.0.0, current version 6.4.2)
/Users/chell/.conan/data/libgphoto2/2.5.27/_/_/package/8880a3412c5909fb46130c128d3ff83f4602ce9e/lib/libgphoto2.6.dylib (compatibility version 8.0.0, current version 8.0.0)
/Users/chell/.conan/data/libgphoto2/2.5.27/_/_/package/8880a3412c5909fb46130c128d3ff83f4602ce9e/lib/libgphoto2_port.12.dylib (compatibility version 13.0.0, current version 13.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1953.255.0)
/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 60420.60.24)
/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1300.36.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
The logs of CMake loading the project contains this -- Conan: Adjusting default RPATHs Conan policies which makes me believe maybe Conan erases the default value that worked before ?
Here are some more details of my system :
MacOS 12.6.3 (but I'll be crosscompiling later to raspberry and I created a github action CI)
Qt6 v6.4.2 installed in home folder
Conan 1.58.0
Cmake 3.25.1
Thank you very much for your help.
Benjamin
cmake file
cmake_minimum_required(VERSION 3.13)
project(p1)
set(CMAKE_CXX_STANDARD 11)
FIND_PACKAGE(PythonInterp)
if (PYTHONINTERP_FOUND)
if (UNIX AND NOT APPLE)
if (PYTHON_VERSION_MAJOR EQUAL 3)
FIND_PACKAGE(Boost COMPONENTS python${PYTHON_VERSION_SUFFIX})
FIND_PACKAGE(PythonInterp 3)
FIND_PACKAGE(PythonLibs 3 REQUIRED)
else()
FIND_PACKAGE(Boost COMPONENTS python)
FIND_PACKAGE(PythonInterp)
FIND_PACKAGE(PythonLibs REQUIRED)
endif()
else()
if (PYTHON_VERSION_MAJOR EQUAL 3)
FIND_PACKAGE(Boost COMPONENTS
python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR})
FIND_PACKAGE(PythonInterp 3)
FIND_PACKAGE(PythonLibs 3 REQUIRED)
else()
FIND_PACKAGE(Boost COMPONENTS
python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR})
FIND_PACKAGE(PythonInterp)
FIND_PACKAGE(PythonLibs REQUIRED)
endif()
endif()
else()
message("Python not found")
endif()
message(STATUS "PYTHON_LIBRARIES = ${PYTHON_LIBRARIES}")
message(STATUS "PYTHON_EXECUTABLE = ${PYTHON_EXECUTABLE}")
message(STATUS "PYTHON_INCLUDE_DIRS = ${PYTHON_INCLUDE_DIRS}")
message(STATUS "Boost_LIBRARIES = ${Boost_LIBRARIES}")
#ENABLE_TESTING()
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS})
add_library(pylib SHARED pylib.cpp)
target_link_libraries(pylib ${Boost_LIBRARIES} ${PYTHON_LIBRARIES})
#
# Tweaks the name of the library to match what Python expects
set_target_properties(pylib PROPERTIES SUFFIX .so)
set_target_properties(pylib PROPERTIES PREFIX "")
cmake output:
/Applications/CLion.app/Contents/bin/cmake/mac/bin/cmake -
DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - Unix Makefiles"
/Users/studentuser/CLionProjects/sbmlPythonAPI
-- Found PythonInterp: /usr/local/bin/python (found version "2.7.16")
-- Boost version: 1.68.0
-- Found the following Boost libraries:
-- python27
-- PYTHON_LIBRARIES = /usr/lib/libpython2.7.dylib
-- PYTHON_EXECUTABLE = /usr/local/bin/python
-- PYTHON_INCLUDE_DIRS =
/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr
/include/python2.7
-- Boost_LIBRARIES = /usr/local/lib/libboost_python27-mt.dylib
-- Configuring done
-- Generating done
-- Build files have been written to:
/Users/studentuser/CLionProjects/sbmlPythonAPI/cmake-build-debug
Bonjour.hpp
#include <iostream>
#include <string>
using namespace std;
class Bonjour
{
// Private attribute
string m_msg;
public:
// Constructor
Bonjour(string msg):m_msg(msg) { }
// Methods
void greet() { std::cout << m_msg << std::endl; }
void check_func() {cout<<"Hello! I am working"; }
// Getter/Setter functions for the attribute
void set_msg(std::string msg) { this->m_msg = msg; }
std::string get_msg() const { return m_msg; }
};
pylib.cpp
#include <boost/python.hpp>
#include "Bonjour.hpp"
using namespace boost::python;
BOOST_PYTHON_MODULE(pylib)
{
class_< Bonjour >("Bonjour", init<std::string>())
.def("greet", &Bonjour::greet)
.add_property("msg", &Bonjour::get_msg, &Bonjour::set_msg);
}
I get the error message stated in the tile when I try running
from pylib import Bonjour
b = Bonjour("He")
Error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-2-a019b42ef03f> in <module>()
----> 1 b = Bonjour("He")
TypeError: __init__() should return None, not 'NoneType'
I am using macOS, and I also encounter this TypeError recently! It is probably caused by linking the built .so file to a different Python interpreter's lib/libpython2.7.dylib file.
1. First checkout your .so file by using otool -L command:
$ otool -L libh264decoder.so
libh264decoder.so:
/somepath/build/libh264decoder.so (compatibility version 0.0.0, current version 0.0.0)
/usr/local/opt/ffmpeg/lib/libavcodec.58.dylib (compatibility version 58.0.0, current version 58.35.100)
/usr/local/opt/ffmpeg/lib/libswscale.5.dylib (compatibility version 5.0.0, current version 5.3.100)
/usr/local/opt/ffmpeg/lib/libavutil.56.dylib (compatibility version 56.0.0, current version 56.22.100)
/usr/local/opt/boost-python/lib/libboost_python27-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
/opt/local/Library/Frameworks/Python.framework/Versions/2.7/Python (compatibility version 2.7.0, current version 2.7.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)
Note that this .so file is linked to the Python installed by MacPort, which is located at: /opt/local/Library/Frameworks/Python.framework/Versions/2.7/Python
2. But the Python interpreter I am running is a different one.
It's located at: /Users/name/anaconda3/envs/py27/ (which apparently is installed by Anaconda).
3. So the solution is to let cmake link to the right Python library by setting DPYTHON_LIBRARY varible:
$ cd build/
$ cmake -DPYTHON_LIBRARY="/Users/name/anaconda3/envs/py27/lib/libpython2.7.dylib" ..
$ make
4. Finally, check the result:
$ otool -L libh264decoder.so
libh264decoder.so:
/somepath/build/libh264decoder.so (compatibility version 0.0.0, current version 0.0.0)
/usr/local/opt/ffmpeg/lib/libavcodec.58.dylib (compatibility version 58.0.0, current version 58.35.100)
/usr/local/opt/ffmpeg/lib/libswscale.5.dylib (compatibility version 5.0.0, current version 5.3.100)
/usr/local/opt/ffmpeg/lib/libavutil.56.dylib (compatibility version 56.0.0, current version 56.22.100)
/usr/local/opt/boost-python/lib/libboost_python27-mt.dylib (compatibility version 0.0.0, current version 0.0.0)
#rpath/libpython2.7.dylib (compatibility version 2.7.0, current version 2.7.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)
Note that the libpython2.7.dylib's linking path is changed.
I'm trying to use OpenMP on Mac.
After compiling, when running binary file,
I get
dyld: Library not loaded: #rpath/libomp.dylib
Referenced from: ./lab1
Reason: image not found
[1] 64552 trace trap ./lab1
I used otool to find out what path it expects.
otool -L lab1
lab1:
#rpath/libomp.dylib (compatibility version 5.0.0, current version 5.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)
This required library libomp.dylib is at path /usr/local/opt/llvm/lib.
How to make the binary can find it ?
Try adding the required path to the executable's rpaths by using a CMake POST_BUILD action:
add_custom_command(TARGET lab1
POST_BUILD COMMAND
${CMAKE_INSTALL_NAME_TOOL} -add_rpath /usr/local/opt/llvm/lib
$<TARGET_FILE:lab1>)
This only works if lab1 is a CMake executable target that is created with add_executable.
Is there a way to specify the shared library path from CMakeLists.txt?
I want to make an executable which uses OpenCV but I don't want the final user to install OpenCV. Instead I would like to deliver a folder containing my executable and the shared libraries it needs. The final structure should be something like
Delivery
MyApp
OpenCV (folder containing opencv dylibs)
In the end MyApp should search for the shared libs in OpenCV folder.
I tried to use the info from https://cmake.org/Wiki/CMake_RPATH_handling but for some reason I don't seem to understand what i really have to do to achieve what I want.
I tried to set CMAKE_INSTALL_RPATH to #loader_path or #executable_path but don't see any change. It still searches for opencv libs in /lib folder.
Running otool -L MyApp results in:
lib/libopencv_imgproc.3.1.dylib (compatibility version 3.1.0, current version 3.1.0)
lib/libopencv_imgcodecs.3.1.dylib (compatibility version 3.1.0, current version 3.1.0)
lib/libopencv_highgui.3.1.dylib (compatibility version 3.1.0, current version 3.1.0)
lib/libopencv_core.3.1.dylib (compatibility version 3.1.0, current version 3.1.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)
I want to create a custom framework with Qt. I am able to create the framework itself, but Qt fails to find the framework when run:
dyld: Library not loaded: QSettingsDialog.framework/Versions/0/QSettingsDialog
Referenced from: /Users/sky/QtProjects/build-QSettingsDialog-Desktop_Qt_5_7_0_clang_64bit-Debug/Examples/SimpleExample/SimpleExample.app/Contents/MacOS/SimpleExample
Reason: image not found
The reason is simple: The binary does not know where to look for the framework.
I used otool on the binary and saw this:
otool -L Examples/SimpleExample/SimpleExample.app/Contents/MacOS/SimpleExample
Examples/SimpleExample/SimpleExample.app/Contents/MacOS/SimpleExample:
QSettingsDialog.framework/Versions/0/QSettingsDialog (compatibility version 0.1.0, current version 0.1.2)
#rpath/QtWidgets.framework/Versions/5/QtWidgets (compatibility version 5.7.0, current version 5.7.0)
#rpath/QtGui.framework/Versions/5/QtGui (compatibility version 5.7.0, current version 5.7.0)
#rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.7.0, current version 5.7.0)
/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)
So, my question is as follows (For more details check the stuff below):
How can tell qmake to set the path for my library to:
#rpath/QSettingsDialog.framework/Versions/0/QSettingsDialog (compatibility version 0.1.0, current version 0.1.2)
Is there even a way to do this with qmake? I do know how to change this with the install_name_tool, but I would like to add it to the .pro file directly.
What I did so far:
I modified my pro file to change to rpath of the binary to include the path where the library is located at build-time:
otool -l Examples/SimpleExample/SimpleExample.app/Contents/MacOS/SimpleExample
Load command 22
cmd LC_RPATH
cmdsize 136
path /Users/sky/QtProjects/build-QSettingsDialog-Desktop_Qt_5_7_0_clang_64bit-Debug/Examples/SimpleExample/../../QSettingsDialog (offset 12)
Load command 23
cmd LC_RPATH
cmdsize 48
path /Users/sky/Qt/5.7/clang_64/lib (offset 12)
This way I can simply modify the rpath for a release without having to use the install_name_tool. However, for this to work, I need to change the first line to:
#rpath/QSettingsDialog.framework/Versions/0/QSettingsDialog (compatibility version 0.1.0, current version 0.1.2)
In my pro file for the application, i specified the following:
mac {
QMAKE_LFLAGS += -F$$OUT_PWD/../../QSettingsDialog/
QMAKE_LFLAGS += '-Wl,-rpath,\'$$OUT_PWD/../../QSettingsDialog\''
LIBS += -F$$OUT_PWD/../../QSettingsDialog/ -framework QSettingsDialog
}
The last missing piece is how to add the #rpath/. Thanks for your help.
Thanks to the links in the comment of #peppe, I was able to solve the problem:
I had to add
QMAKE_LFLAGS_SONAME = -Wl,-install_name,#rpath/
to the libraries pro file. This way Qt automatically uses #rpath/QSettingsDialog.framework/Versions/0/QSettingsDialog when creating the reference to the framework.