How to link a shared library with CMake with relative path - c++

I want to link a third-party libLibrary.so and distribute it with my program. If user unzips my archive, he will get this folder structure:
game
libLibrary.so
game_executable
game_executable depends on ./libLibrary.so.
My project structure:
game
bin
libLibrary.so
lib
Library.h
src
game_executable.cpp
CMakeLists.txt
My CMakeLists.txt:
cmake_minimum_required(VERSION 3.7)
project(game)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/bin)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(SOURCE_FILES src/game_executable.cpp)
include_directories(${CMAKE_SOURCE_DIR}/lib)
add_executable(game ${SOURCE_FILES})
target_link_libraries(${PROJECT_NAME} ${CMAKE_BINARY_DIR}/libLibrary.so)
However, what I get is my game_executable depends on the .../game/bin/libLibrary.so, not on the ./libLibrary.so that is in the folder with game_executable, making this totally unportable!
How can I make linking path relative instead of absolute?

From the documentation:
By default if you don't change any RPATH related settings, CMake will link the executables and shared libraries with full RPATH to all used libraries in the build tree.
This is the behaviour you are seeing.
However, there are a number of ways to change this to match the behaviour you require.
Some examples from the above linked docs:
# use, i.e. don't skip the full RPATH for the build tree
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
# when building, don't use the install RPATH already
# (but later on when installing)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
# the RPATH to be used when installing
SET(CMAKE_INSTALL_RPATH "")
# don't add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
Using the above you'll likely want to set CMAKE_INSTALL_RPATH and then distribute the installed binary.
If you want to distribute from the binary in your build tree, it is also possible to bypass CMake's rpath handling and modify the rpath directly using linker flags:
set_target_properties(game PROPERTIES LINK_FLAGS "-Wl,-rpath,./")

Most of the time you want to set the RPATH to $ORIGIN instead of ., because it refers to the executable's path instead while . refers to the current directory at runtime (which can be anything else).
I find it simple to edit LINK_FLAGS instead of INSTALL_RPATH target property, because CMakes already has a variable named ORIGIN (see CMake's documentation).
So that boils down to the following:
# Find shared libraries next to the executable
set_target_properties(target_name PROPERTIES
BUILD_WITH_INSTALL_RPATH FALSE
LINK_FLAGS "-Wl,-rpath,$ORIGIN/")

Talking about distribution the executable or shared library with dynamically linked libraries and CMake build system:
SET(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE)
This var forces linking with relative path within the build tree, so in result the build directory can be movable.
If you use this command on linux machine
find <YOUR_TARGET_NAME> -type f -perm /a+x -exec ldd {} \; | grep so | sed -e '/^[^\t]/ d' | sed -e 's/\t//' | sed -e 's/.*=..//' | sed -e 's/ (0.*)//' | sort | uniq -c | sort -n
you will see the dot in the path, which indicates the relativity, example:
~/project/build/./lib/my_shared_lib.so
See more in CMake docs.

If you use target_link_directories then cmake will add it to the linker command as a manual rpath

Related

Why CMake puts the absolute path of my IMPORTED library inside my binaries?

For using find_package( for another library that I am using (it is not supporting CMake), I have developed something like this (I am using gcc10 on CentOS 7 and CMake 3.20 with Ninja 1.10.2):
add_library(MyLib UNKNOWN IMPORTED)
target_include_directories(MyLib
INTERFACE "$ENV{MY_LIB}/include")
target_link_libraries(MyLib INTERFACE ${CMAKE_DL_LIBS})
set_property(TARGET MyLib PROPERTY
IMPORTED_LOCATION "$ENV{MY_LIB}/lib/libmylib.so"
)
and in my project, I simply use find_package and link against it:
find_package(MyLib REQUIRED PATHS "${CMAKE_MODULE_PATH}" NO_DEFAULT_PATH)
target_link_libraries(myApp
PUBLIC MyLib
)
The strange behavior that I see with this code is that the linker puts the absolute path of libmylib.so in myApp target. In other words, if I copy/move my whole project into a new folder, it fails to load the library:
/data/projects/myApp/external/myLib/lib/libmylib.so: cannot open shared object file: No such file or directory
Can anyone tell me why the path to 'libmylib.so' is hard-coded?
Note: the LD_LIBRARY_PATH is already set and valid.
Update 1: If I run readelf -d myApp | head -20, I will see this result:
[user]$ readelf -d myApp | head -20
Dynamic section at offset 0x101588 contains 73 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libsth1.so]
0x0000000000000001 (NEEDED) Shared library: [libsth2.so]
0x0000000000000001 (NEEDED) Shared library: [/data/projects/myApp/external/myLib/lib/libmylib.so]
Update 2: I have tried to unset these two variables, but nothing changes:
set(CMAKE_SKIP_BUILD_RPATH OFF)
set(CMAKE_SKIP_RPATH OFF)
There are multiple ways how to get your executeable to work:
(1) Edit LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"PATH_TO_SO"
will allow the dynamic linker to find and link the shared object.
(2) Edit RPATH
# Make sure required .so files are found in the same folder as the shared library
set_target_properties(myApp PROPERTIES
BUILD_WITH_INSTALL_RPATH TRUE
#INSTALL_RPATH_USE_LINK_PATH TRUE
INSTALL_RPATH "\$ORIGIN"
)
I would prefer (2) if you want to move your app around in the file system.
Update explanation:
cmake tries to get your build to work. Internally it usually uses absolute paths. Generated cmake projects are not copyable to other places in the filesystem, but have to regenerated.
find_package() returns the absolute path to your library. Its the only assumption it can make. Just adding the library name without the full path could lead to using a similar named but wrong (incompatible, wrong version) shared object.
It is your task to prepare everything in your CMakeLists install section. Those rules should describe what to copy and how to prepare encompanied shared object libraries.
Packaging systems like rpm or dhbuild depend on it.
I hope that is helpful.
Bye Gunther

How to correctly set rpath to shared library with CMake?

How can I link OpenNI (libOpenNI2.so) at run time to my C++ program? This question/answer is most relevant to my question. I followed it and prepared the following CMakeLists.txt but still it cannot link the .so file and generates an error /usr/bin/ld: cannot find -lOpenNI2
I use cmake .. && cmake --build . --config Release to compile the program.
I tried $ORIGIN, $$ORIGIN, \$ORIGIN and I noticed that ORIGIN is empty string.
What am I doing wrong?
cmake_minimum_required(VERSION 3.1)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
project(rgbd)# project name
# to link OpenNI2 at runtime
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
SET(CMAKE_INSTALL_RPATH "")
add_executable(rgbd rgbd.cpp)
message(STATUS ${ORIGIN})# display ORIGIN
set_target_properties(rgbd PROPERTIES LINK_FLAGS "-Wl,-rpath,$ORIGIN/../OpenNI-Linux-x64-2.3.0.66/Redist")
target_link_libraries(rgbd libOpenNI2.so)
The error you get isn't at runtime but at link time. ld cannot find the specified libOpenNI2.so because you haven't provided any search path to the linker.
You shouldn't have to do anything special as CMake will use build rpath by default (that gets removed during installation, but this is not a step that you've configured anyway).
This should be enough:
cmake_minimum_required(VERSION 3.13)
project(rgbd)
add_executable(${PROJECT_NAME} rgbd.cpp)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_11)
target_link_directories(${PROJECT_NAME} PRIVATE ../OpenNI-Linux-x64-2.3.0.66/Redist)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenNI2)
cd path/to/project
cmake -B build/Release -DCMAKE_BUILD_TYPE=Release
cmake --build build/Release
./build/Release/rgbd
Now if you're going to ship your executable, consider adding a correct installation step with install rpath handled:
cmake_policy(SET CMP0095 OLD) //CMake>=3.16 handles $ORIGIN escaping differently
set_target_properties(${PROJECT_NAME}
PROPERTIES
INSTALL_RPATH "\\\$ORIGIN/../lib"
)
include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME})
install(FILES ../OpenNI-Linux-x64-2.3.0.66/Redist/libOpenNI2.so TYPE LIB)
cmake -B build/Release -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$PWD/package
cmake --build build/Release --target install
./package/bin/rgbd
The rpath is information that's only used at runtime to find the lib. The linker will not use it to determine the location of the location of libraries to link.
With cmake you could specify an absolute path for target_link_libraries here or add a link directory and let the linker figure out which file to use.
Path to lib
# not sure if this is the exact path; you may need to do some adjustments
target_link_libraries(rgbd "${CMAKE_CURRENT_SOURCE_DIR}/../OpenNI-Linux-x64-2.3.0.66/Redist/libOpenNI2.so")
Link directory
# again not sure, if the path is correct here
target_link_directories(rgdb PRIVATE ../OpenNI-Linux-x64-2.3.0.66/Redist)
target_link_libraries(rgbd PRIVATE OpenNI2)
If you're linking the lib to multiple targets, using an imported library may be a good idea, since it allows you to add info like include directories and additional dependencies to the lib.
add_library(OpenNI2_import SHARED IMPORTED GLOBAL)
set_target_properties(OpenNI2_import PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../OpenNI-Linux-x64-2.3.0.66/Redist/libOpenNI2.so"
)
# include directories could be added
target_include_directories(OpenNI2_import INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/../OpenNI-Linux-x64-2.3.0.66/include" # probably not the correct path; use absolute paths here
)
# could add dependencies, if necessary
target_link_libraries(OpenNI2_import INTERFACE some_other_lib)
...
target_link_libraries(rgbd PRIVATE OpenNI2_import)
You may still need to adjust the rpath, but using \$ORIGIN/... should work to get "cmake" to put $ORIGIN/... in the rpath of the resulting binary.

cmake: how to install the include/ and lib/ directories as autotool does

I have an old project and I want to make use of cMake instead of the old autotools.
What the old program does is that, after type make, it will make libtest.a, libtest.la, libtest.so.1.0.0 etc. inside a hidden folder called .libs, then after I type make install, it will intall all libraries to a target folder $TEST_ROOT/lib (environment variable), it will also install all .h files into $TEST_ROOT/include folder.
in the Makefile.am:
source_list=test1.cpp test2.cpp
include_HEADERS=test1.h test2.h
AM_LDFLAGS="-pthread -lm -lrt"
lib_LTLIBRARIES=libtest.la
libtest_la_SOURCES=$(source_list)
libtest_la_LDFLAGS=$(AM_LDFLAGS) -static-libgcc
in configure.ac, I only see one relevant line,
if test -n "${TEST_ROOT}"; then
ac_default_prefix=${TEST_ROOT}
includedir=${ac_default_prefix}/include
fi
Frankly I don't really understand why the above codes will make .a, .la, .so etc. libraries all together, and then install them into the curresponding folder. Probably autotools can recognize the "ac_default_prefix" and "includeddir"?
Anyway, I want to do the same thing with cmake, the following is my attempt, but not a complete solution.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/.libs)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY $(CMAKE_BINARY_DIR}/.libs)
set(CMAKE_CXX_FLAGS "-O3 -Wall")
set(CMAKE_EXE_LINKER_FLAGS "-pthread -lm -lrt")
file(GLOB SOURCES "*.cpp")
add_library(test STATIC ${SOURCES})
The above code will compile libtest.a in the build folder, not in the .libs folder inside build folder (meaning that CMAKE_RUNTIME_OUTPUT_DIRECTORY etc doesn't work).
Secondly, it will only build libtest.a, there is no libtest.la, no libtest.so.1.0.0 etc.
Thirdly, I am still not sure how let make install work like the auto tools. Can I just set the target include directory and target lib directory, then make install will install all .h files and .so, .a, .la files into the target directory?
Please help.
Thanks.
You have to go to the coreesponding CMakeLists.txt and add e.g.
INSTALL(TARGETS test DESTINATION lib)
In your root CMakeLists.txt you can determine the standard installation path:
SET(CMAKE_INSTALL_PREFIX ".")
In a similar way you can install your header files:
FILE(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/*.hxx")
INSTALL(FILES ${files} DESTINATION include)
You can find even more examples at: https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/Install-Commands
You can then install the files and libs with make (https://cmake.org/cmake/help/v3.1/variable/CMAKE_INSTALL_PREFIX.html):
make DESTDIR=/home/mistapink install
You might need to set CMAKE_ARCHIVE_OUTPUT_DIRECTORY too.
Aside from that, be aware that CMake doesn't work like autotools. If you want multiple libraries you need multiple add_library calls.

Cmake on OSX Yosemite 10.10.3 - GLEW: package 'gl' not found

A while ago I decided to learn and delve myself in Cmake and ran into some annoying issues.
Following some tutorials (involving opengl and glew), I built a basic application that ran fine on a windows emulated machine but at a specific moment, emulation wouldn't just do the trick anymore and I switched to Xcode and also Cmake.
So I set up a basic folder structure:
build
CMakeLists.txt
inc
lib
src
lib folder includes the folders:
glew
glfw
Which are unzipped straight from their original downloads (including a CMakeLists.txt file and all)
To include the libs I used these 2 lines in my root CMakeLists.txt but only with glew I got a "'gl' not found" error
ADD_SUBDIRECTORY(lib/glfw)
ADD_SUBDIRECTORY(lib/glew)
When building with cmake I got this output which I wasn't very happy about :(
Could NOT find Doxygen (missing: DOXYGEN_EXECUTABLE)
Using Cocoa for window creation
Using NSGL for context creation
Building GLFW only for the native architecture
checking for module 'gl'
package 'gl' not found
CMake Error at /Applications/CMake.app/Contents/share/cmake-3.2/Modules/FindPkgConfig.cmake:340 (message):
A required package was not found
Call Stack (most recent call first):
/Applications/CMake.app/Contents/share/cmake-3.2/Modules/FindPkgConfig.cmake:502 (_pkg_check_modules_internal)
lib/glew/CMakeLists.txt:26 (pkg_check_modules)
Configuring incomplete, errors occurred!
See also "/Users/Makkura/Dropbox/PROGRAMMING/CPLUSPLUS_SANDBOX/CMAKE/SPARKY/build/Xcode/CMakeFiles/CMakeOutput.log".
I assume there is something wrong with the CMakeLists.txt file in the GLEW folder. I can see specific actions for windows and linux but I can't see anything specified for osx systems.
project(GLEW)
cmake_minimum_required(VERSION 2.4)
if(COMMAND cmake_policy)
cmake_policy(SET CMP0003 NEW)
endif(COMMAND cmake_policy)
set(GLEW_VERSION "1.11.0")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(GLEW_LIB_NAME glew32)
else(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(GLEW_LIB_NAME GLEW)
set(DLL_PREFIX lib)
endif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
#
# All platforms need OpenGL
#
include(FindPkgConfig)
pkg_check_modules( OpenGL REQUIRED gl )
#
# Linux needs X11
#
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
find_package(X11 REQUIRED)
endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(CMAKE_C_FLAGS "${CFLAGS} ${CMAKE_C_FLAGS} -DGLEW_BUILD -DGLEW_NO_GLU -O2 -Wall -W" )
include_directories( ${PROJECT_SOURCE_DIR}/include )
add_library(GLEW_static STATIC src/glew.c )
add_library(GLEW_shared SHARED src/glew.c )
set_target_properties(GLEW_static PROPERTIES OUTPUT_NAME ${GLEW_LIB_NAME} PREFIX lib)
set_target_properties(GLEW_shared PROPERTIES OUTPUT_NAME ${GLEW_LIB_NAME} PREFIX "${DLL_PREFIX}")
target_link_libraries(GLEW_shared ${OpenGL_LDFLAGS})
add_library(GLEW_MX_static STATIC src/glew.c )
add_library(GLEW_MX_shared SHARED src/glew.c )
set_target_properties(GLEW_MX_static PROPERTIES OUTPUT_NAME ${GLEW_LIB_NAME}mx COMPILE_FLAGS "-DGLEW_MX" PREFIX lib)
set_target_properties(GLEW_MX_shared PROPERTIES OUTPUT_NAME ${GLEW_LIB_NAME}mx COMPILE_FLAGS "-DGLEW_MX" PREFIX "${DLL_PREFIX}")
target_link_libraries(GLEW_MX_shared ${OpenGL_LDFLAGS})
add_executable(glewinfo src/glewinfo.c)
target_link_libraries(glewinfo GLEW_shared ${OpenGL_LDFLAGS})
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
target_link_libraries(glewinfo ${X11_LIBRARIES})
endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
add_executable(visualinfo src/visualinfo.c)
target_link_libraries(visualinfo GLEW_shared ${OpenGL_LDFLAGS})
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
target_link_libraries(visualinfo ${X11_LIBRARIES})
endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
install(
TARGETS
GLEW_static
GLEW_shared
GLEW_MX_static
GLEW_MX_shared
glewinfo
visualinfo
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
install(CODE "execute_process( COMMAND bash -x -c \"sed -e 's%#prefix#%${CMAKE_INSTALL_PREFIX}%g' -e 's%#exec_prefix#%\\\${prefix}%g' -e 's%#libdir#%\\\${prefix}/lib%g' -e 's%#includedir#%\\\${prefix}/include%g' -e 's/\#version\#/${GLEW_VERSION}/g' -e 's/\#cflags\#//g' -e 's/\#libname\#/${GLEW_LIB_NAME}/g' -e 's|#requireslib#|glu|g' < ${CMAKE_SOURCE_DIR}/glew.pc.in > ${CMAKE_BINARY_DIR}/glew.pc\" )" )
install(CODE "execute_process( COMMAND bash -x -c \"sed -e 's%#prefix#%${CMAKE_INSTALL_PREFIX}%g' -e 's%#exec_prefix#%\\\${prefix}%g' -e 's%#libdir#%\\\${prefix}/lib%g' -e 's%#includedir#%\\\${prefix}/include%g' -e 's/\#version\#/${GLEW_VERSION}/g' -e 's/\#cflags\#/-DGLEW_MX/g' -e 's/\#libname\#/${GLEW_LIB_NAME}mx/g' -e 's|#requireslib#|glu|g' < ${CMAKE_SOURCE_DIR}/glew.pc.in > ${CMAKE_BINARY_DIR}/glewmx.pc\" )" )
install(FILES ${CMAKE_BINARY_DIR}/glew.pc ${CMAKE_BINARY_DIR}/glewmx.pc DESTINATION lib/pkgconfig)
As long as I don't get this problem solved I can't continue with my development. I've been searching for similar issues for days but I can't find any definitive solution. Has anyone encountered a similar problem like this?
Well, what fails is pkg-config, trying to find the gl package, i.e. a gl.pc configuration file.
I have no idea where that one's supposed to be and there seems to be no documentation regarding it, as well as no reports of anyone ever even trying this.
I can only assume that the goal of this is to reference the OpenGL framework in /System/Library/Frameworks, but, at least on my machine, there is no gl.pc anywhere to be found.
Also, trying to build glew with
export CC='clang'
export CXX='clang++'
cd glew
cmake .
make
gives me the same error you get.
However, I was able to craft my own gl.pc:
PACKAGE=GL
Name: OpenGL
Description: OpenGL
Version: 11.1.1
Cflags: -framework OpenGL -framework AGL
Libs: -Wl,-framework,OpenGL,-framework,AGL
(The AGL framework is part of the OpenGL framework.)
Putting this file in a location where pkg-config would find it, I was able to actually build glew.
Since you are building your own cmake thingy, you might not want to put this file in /usr/share/pkgconfig but rather have it somewhere in your project folder and set PKG_CONFIG_PATH accordingly.
E.g. if you put gl.pc in <your project>/pkgconfig, add this to the top of your CMakeLists.txt:
set(ENV{PKG_CONFIG_PATH} ${CMAKE_SOURCE_DIR}/pkgconfig)
Again, I have no idea whether or not this is the way anything's supposed to be done, but it works (at least for me).

Building of executable and shared library with cmake, runtimelinker does not find dll

I am working with gcc(cygwin), gnu make, windows 7 and cmake.
my cmake testprojekt has the following structure
rootdir
|-- App
| |-- app.cpp
| +-- CMakeLists.txt
|-- Lib
| |-- lib.cpp
| |-- CMakeLists.txt
|-- MakeFileProject
+ CMakeLists.txt
rootdir/App/app.cpp:
#include<string>
void printThemMessageToScreen(std::string input);//prototype
int main(int argc,char **argv){
printThemMessageToScreen("this will be displayed by our lib");
return 0;
}
rootdir/Lib/lib.cpp:
#include<iostream>
#include<string>
void printThemMessageToScreen(std::string input){
std::cout<<input;
}
rootdir/CMakeLists.txt:
cmake_minimum_required(VERSION 2.6)
project(TestProject)
add_subdirectory(App)
add_subdirectory(Lib)
rootdir/Lib/CMakeLists.txt:
add_library(Lib SHARED lib.cpp)
rootdir/App/CMakeLists.txt:
# Make sure the compiler can find include files from our Lib library.
include_directories (${LIB_SOURCE_DIR}/Lib)
# Make sure the linker can find the Lib library once it is built.
link_directories (${LIB_BINARY_DIR}/Lib)
# Add executable called "TestProjectExecutable" that is built from the source files
add_executable (TestProjectExecutable app.cpp)
# Link the executable to the lib library.
target_link_libraries (TestProjectExecutable Lib)
Now, when i run cmake and make, everything will get generated & built with no errors, but when i try to execute the binary, it will fail because the library which was generated could not be found.
BUT: when i copy the lib dll into the same directory like the app exe, it will get executed!
also: if i configure the library to be static, it will also execute.
how to tell the runtime linker where to look for my dll?
UPDATE:
Solution according to the Method proposed by User Vorren:
I opened up the registry editor, and navigated to the following Key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
, here i created a new key with the name of my Applikation:
in this case : TestProjectExecutable.exe
after that, the (default) value was set to the full path of TestProjectExecutable.exe including the filename and extension. Then i created another String Value called "Path" and set the value to the folder where the dll was located:
Your problem lies not with linker or compiler, but with the way Windows searches for DLL's.
The OS will use the following algorithm to locate the required DLL's:
Look in:
The directories listed in the Application-specific Path registry key;
The directory where the executable module for the current process is located;
The current directory;
The Windows system directory;
The Windows directory;
The directories listed in the PATH environment variable;
Thus you have two reasonable options if you don't want to clutter the OS directories with your app-specific dll:
Create an app-specific Path registry entry (I would go with this option);
Put your DLL in the same folder as your EXE;
Modify the PATH variable (but why would you do that, if you can go with option 1?);
A solution I prefer that hasn't really been mentioned, is build your shared-libs into the same directory as your executables. This tends to be a much simpler solution.
One way to do this with cmake is
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
Or you can also set output directories based on build flavours.
See how do I make cmake output into a 'bin' dir?
I discovered (what I believe to be) quite a nice way of handling this. It follows the approach of adding the .dll to the same directory as the .exe. You can do it in CMake like so:
if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
TARGET <app-target> POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
$<TARGET_FILE_DIR:<lib-target>>
$<TARGET_FILE_DIR:<app-target>)
endif()
where app-target is the name of the application or library you're building (created through add_executable or add_library) and lib-target is the imported library brought in with find_package.
# full example
cmake_minimum_required(VERSION 3.14)
project(my-app-project VERSION 0.0.1 LANGUAGES CXX)
find_package(useful-library REQUIRED)
add_executable(my-application main.cpp)
target_link_libraries(my-application PUBLIC useful-library::useful-library)
if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
TARGET my-application POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
$<TARGET_FILE_DIR:useful-library::useful-library>
$<TARGET_FILE_DIR:my-application>)
endif()
I tried the option 1 from accepted answer (by pdeschain).
I even created a cmake hook to register paths of linked libraries automatically
function (xtarget_link_libraries target libs) # same as target_link_libraries but with additional improvements to allow windows find the library at runtime
LIST(REMOVE_AT ARGV 0)
SET(LIBS ${ARGV}) # this is to pass list into this function
target_link_libraries(${target} ${LIBS}) # call standard routine
if(WIN32)
set(TFILE ".")
get_property(slibs TARGET ${target} PROPERTY all_libs) # recall libs linked before
set(LIBS ${slibs};${LIBS})
set_property(TARGET ${target} PROPERTY all_libs ${LIBS}) # save all libs
FOREACH(lib ${LIBS}) # compose a list of paths
set(TFILE "${TFILE};$<TARGET_LINKER_FILE_DIR:${lib}>")
ENDFOREACH()
#add reg key
add_custom_command(TARGET ${target} POST_BUILD COMMAND reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${target}.exe" /v "Path" /d "${TFILE}" /f )
endif()
endfunction()
Can be used as xtarget_link_libraries(test lib1 lib2). The application will be able to find dynamic libraries at their absolute paths.
BUT, there is a big problem with this, that the App Paths mechanism https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx#appPaths
does not allow to have different entries for say 'Debug/test.exe' and 'Release/test.exe'. So to me this is a poor option.
You may add the following line to fill the Default key as path to the program as suggested in the post.
add_custom_command(TARGET ${target} POST_BUILD COMMAND reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${target}.exe" /ve /d "$<TARGET_FILE:${target}>" /f )
Now you can enjoy running test.exe from anywhere in the system... I guess my next try will be option
Create symbolic links to dlls with cmake.