How to specify linux binary's rpath in cmake - c++

I have a CMakeLists.txt file that contains such line of code:
link_directories(dir_a dir_b dir_c)
When I build an executable and perform
readelf -d
on it, I see RPATH with dir_a, dir_b dir_c.
What can I do with cmake if I want to create
Executable with empty RPATH?
Executable with some paths, but not specified in link_directories line?
The executable is not supposed to be "installed".

From this cmake-documentation you can read that:
you can use the variable CMAKE_SKIP_RPATH to have cmake not adding any RPATH to a binary ever and
that with the SET_TARGET_PROPERTIES-functions RPATHs can be controlled individually on a per-target base.

Related

cmake add_custom_command + Xcode: multi output = multi command invocation

I have custom command that generate several headers at once.
All works fine with make/ninja files generated by cmake.
But if I generate Xcode project via cmake -GXCode,
then instead of once gen.sh was invoked 10 times
and not only that, it also will be invoked every build,
even if timestamps of generated file are younger then gen_in.txt.
How can I fix this?
project(multi_output)
cmake_minimum_required(VERSION 3.17)
set(MANY_HEADERS test0.h test1.h test2.h test3.h test4.h test5.h test6.h test7.h test8.h test9.h)
add_custom_command(
OUTPUT ${MANY_HEADERS}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/gen.sh
DEPENDS gen_in.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_executable(foo main.cpp ${MANY_HEADERS})
where gen.sh is
#!/bin/sh
echo "gen.sh: start"
sleep 1
echo "gen.sh: hard work done"
for i in `seq 0 9`; do
cat gen_in.txt > test$i.h
sed -i bak s/placeholder/$i/g test$i.h
done
In OUTPUT option relative paths are treated as relative to the binary directory.
That is, your add_custom_command actually doesn't produce the files declared as OUTPUT.
This is why it is called more and more: the build tool finds out that an OUTPUT file does not exist, and runs the COMMAND for build that file. (Many build tool don't check whether the OUTPUT file is actually created.)
For files created in the source directory you need to specify their absolute path in OUTPUT option:
set(MANY_HEADERS test0.h test1.h test2.h test3.h test4.h test5.h test6.h test7.h test8.h test9.h)
# This will be a list of _absolute paths_ to the headers
set(MANY_HEADERS_ABS)
foreach(HEADER ${MANY_HEADERS})
list(APPEND MANY_HEADERS_ABS "${CMAKE_CURRENT_SOURCE_DIR}/${HEADER}")
endforeach()
add_custom_command(
OUTPUT ${MANY_HEADERS_ABS}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/gen.sh
DEPENDS gen_in.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
# In add_executable one could use relative paths too
add_executable(foo main.cpp ${MANY_HEADERS})
Note, that add_executable command may accept relative paths to the file both in source and binary trees.
CMake checks whether a file exists or generated (e.g. with add_custom_command) in the source tree, and if it is, the source path is used. Then similar checks are performed for binary tree. (And if this check fails, CMake will emit an error).
Actually, careful inspecting of the make output may give a hint, whether make rebuilds files in the source tree or in the build tree.
This is what is produced by the original code (remember: make is called from the build directory):
[ 33%] Generating test0.h, test1.h, test2.h, test3.h, test4.h, test5.h, test6.h, test7.h, test8.h, test9.h
And this is what is produced when use absolute paths to the source tree. (Out-of-source build, use build/ subdirectory for build.)
[ 33%] Generating ../test0.h, ../test1.h, ../test2.h, ../test3.h, ../test4.h, ../test5.h, ../test6.h, ../test7.h, ../test8.h, ../test9.h

How to link a shared library with CMake with relative path

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

How to suppress unwanted output build files when using cached variables with a CMakeList.txt file in CMAKE

Currently, I have a CMakeLists.txt file in the main folder that has the following code in it:
cmake_minimum_required(VERSION 3.5)
SET(CMAKE_GENERATOR "MinGW Makefiles" CACHE INTERNAL "" FORCE)
SET(CMAKE_TOOLCHAIN_FILE ${PROJECT_SOURCE_DIR}/ToolChain.cmake)
project(Blinky)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
...
When I run it, it outputs first for Visual Studio to the source directory. When I run it the second time, it outputs the corresponding minGW makefiles but still to the source directory and not the bin folder. Is there any way to configure it to build for MinGW Makefile directly and to the correct output folder?
I'm running the script on a command line prompt with the following line of code from the source directory folder:
cmake CMakeLists.txt
Run cmake from the directory you want to use as your build directory, not from within your source tree. That will give you an out of source build (see here for some details about this).
You have to set the CMake generator and toolchain file you want to use on the command line, you don't do it within the CMakeLists.txt file. Also, you do not include the name of the CMakeLists.txt file on your cmake command line, but rather the directory it is in. For example:
cmake -G "MinGW Makefiles" -DCMAKE_TOOLCHAIN_FILE=/path/to/source/dir/ToolChain.cmake /path/to/source/dir
Lastly, for specifying where your executables should go, make sure you use the correct CMake variables and make sure you put them in a place within your build directory, not your source tree:
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
You shouldn't run cmake from your source directory. It's definitely a bad practice
Indeed, that mixes your makefiles (or your Visual Studio files) with your source files and that can even corrupt your source directory (depending on what you've specified in your CMakeLists.txt).
What you have to do
First you need to create a separate build directory where you'll launch cmake.
Then, if you want to generate with MinGW makefiles, launch in the build directory the following command line:
cmake path_to_source_directory -G "MinGW Makefiles"
Further comments about your CMakeList.txt
As the command line above specifies the generator (option -G "…"), SET(CMAKE_GENERATOR "MinGW Makefiles" CACHE INTERNAL "" FORCE) is useless. Take a look on this SO post and its answers. It explains why you generate Visual Studio files on your first launch of CMake and MinGW makefiles on your second launch.
Then, as #Craig Scott said, you should replace SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) by SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin). Otherwise your executables will be put in your source directory.

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.

CMake: Embed ELF into executable

I have a project that needs access to an ELF file embedded into the executable in a special section.
I was handcrafting Makefiles before and simply had a shell script where I used objcopy to copy the target I wanted to embed into an .o file, then link to this file in the executable.
# Create a new section and copy the binary there ($1=input $2=output name)
objcopy --input-target binary --output-target elf64-x86-64 \
--binary-architecture i386 $1 $2.o
Now I want to get rid of the custom Makefiles and use CMake to generate them. However, I don't see an easy way to link to such a file. I am able to create and add this file, but not to link against it:
# Invoke script to package module as a library
add_custom_command(OUTPUT ${PACKAGED_FILE}
COMMAND ./package.sh ${MODULE_FILE} ${PACKAGED_FILE}
WORKING_DIRECTORY ${MODULE_DIR}
DEPENDS ${MODULE_FILE}
COMMENT packaging file into ELF object
VERBATIM
)
add_custom_target(${PACKAGED_NAME} ALL DEPENDS ${PACKAGED_FILE})
I have tried to add it with:
target_link_libraries(binary ${PROJECT_BINARY_DIR}/${PACKAGED_FILE})
However, this fails because the file isn't there yet. It will be, but CMake doesn't know that. Adding the target name as a link library doesn't help either because it can't be found. Adding it as a also dependency doesn't help. Does anyone have an idea how this could be accomplished?
We are doing a similar thing in our project - the following part of our CMakeLists.txt does the trick:
set(PROJECT_EMBED_OBJ_FILES "")
set(PROJECT_EMBED_FILES "file1.elf" "file2.elf")
foreach(FILENAME ${PROJECT_EMBED_FILES})
get_filename_component(FILENAME_ONLY ${FILENAME} NAME)
get_filename_component(FILEPATH_ONLY ${FILENAME} PATH)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME_ONLY}.o
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/${FILEPATH_ONLY}
COMMAND ${CMAKE_OBJCOPY}
ARGS -I binary -O elf64-x86-64 -B i386 ${FILENAME_ONLY} ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME_ONLY}.o )
list(APPEND PROJECT_EMBED_OBJ_FILES ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME_ONLY}.o)
message(STATUS "Objcopy'ing ${FILENAME}")
endforeach(FILENAME)
And then in the call to add_executable:
add_executable(projectname ${PROJECT_SOURCES} ${PROJECT_EMBED_OBJ_FILES})
You may try
add_custom_command(TARGET $(PROJECT_NAME).elf
POST_BUILD
COMMAND ${CMAKE_OBJCOPY} ARGS -O binary ${PROJECT_NAME}.elf \
${PROJECT_NAME}.bin)
Put this after your add_executable().
The POST_BUILD means execute after build.