CMake : Relink directory and double linking - cmake

In one of my projects at work the following was noticed and asked to me.
A binary would link and then link again in a folder called the "Relink" folder.
I also checked and the binary is in two places and linking occurs twice.
CMAKE version used is 2.6 - patch 2.
I found that in the build folder, the generated cmake_install.cmake has the following entry
IF(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" MATCHES "^(Unspecified)$")
IF("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Dd][Ee][Bb][Uu][Gg])$")
FILE(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/path/to/binary/file/folder/CMakeFiles/CMakeRelink.dir/<binary name>").....
If INSTALL is used in CMAKE, and COMPONENT is not created with the install command AND the build is a Debug build, then this Relink Folder is created and the binary is linked twice. The question is why does this happen?
Linking twice increases your over build time.
So what purpose does this serve.
If the Install command is omitted or the build is a Debug build, then this does not occur.
Why not and why is it relevant if the build is a Debug build or not?
I have searched documentation and this site but could not find an explanation.
example output :
.....
Linking CXX shared library lib<library-name>.so <== Linking the first time
[100%] Built target <another library name>
[ 15%] Built target <another-library-name1>
[ 92%] Built target <another-library-name2>
[ 92%] Built target <another-library-name3>
[100%] Built target <another-library-name4>
[100%] Built target <another-library-name5>
Linking CXX shared library CMakeFiles/CMakeRelink.dir/lib<library-name>.so <== Second linking.
This Relink directory shows up when the following CMAKELists entry is added
install(TARGETS <binary/library name>
RUNTIME
DESTINATION "bin")
So building this as a Debug build and non COMPONENT install shows this behaviour.
Same code when built on Jenkins does not have this because the Jenkins build is a Release/Optimized build and not a Debug build.

Looks like Relinking before installation of binaries has a lot to do with preserving the RPATH in the build.
So I tried to fulfill the initial check for COMPONENT definition and then saw the generated CMake and it shows new conditions to fulfill.
Found some relevant info here
http://public.kitware.com/pipermail/cmake/2006-October/011377.html
So, in the test project where the COMPONENT building is enabled, for INSTALL command of cmake, Re-linking again occurs with another condition to fulfill this time. In this case install_public option is checked, which has been enabled in the project.
IF(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" MATCHES "^(install_public)$")
IF("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Dd][Ee][Bb][Uu][Gg])$")
FILE(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/lib64" TYPE SHARED_LIBRARY
#install CMakeRelink.dir/<library_name>.so.<library_version> ......
Some info here as well in the docs
https://cmake.org/Wiki/CMake_2.8.0_Docs
Search for Re-linking in the above link.
So I tested another build by changin the CMAKELists.txt and setting CMAKE_INSTALL_RPATH to OFF in the application CMAKEList.
This also stopped the double linking.

Related

How to correctly link static library build and installed previously

There is a static library called revolta which is being built and then installed into a sysroot:
set( CMAKE_INSTALL_PREFIX <path to sysroot> )
# ReVolta c++ library name
set( TARGET_LIBREVOLTA "revolta" )
add_library( ${TARGET_LIBREVOLTA} STATIC )
target_include_directories( ${TARGET_LIBREVOLTA}
PUBLIC
# Once the librevolta targets are being exported, this include directory in which the lib is installed is used
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
PRIVATE
# Include directory used privately just to build the library itself
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
)
target_sources( ${TARGET_LIBREVOLTA}
PUBLIC
...
)
Later then once the librevolta is built, it is installed into the sys root using:
# Install all the revolta headers into include directory and copy the built library
install( TARGETS ${TARGET_LIBREVOLTA} EXPORT ${TARGET_LIBREVOLTA}
FILE_SET HEADERS DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
and the connected custom command:
# Once the librevolta is built, install it to the sysroot as specified by 'install()' commands
add_custom_command( TARGET ${TARGET_LIBREVOLTA} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS --install . )
So far so good. This works as intended, once CMake builds the "revolta" target, it is built and installed into the sysroot as installed using the ${CMAKE_INSTALL_PREFIX}.
My problem is once I try to add the target as the linked one in other lib/executable, it includes somehow automatically the librevolta source path into includes and links the library using the relative path in the build directory rather than the one installed into sysroot as performed in the step right after the librevolta build.
Some other lib/executable:
target_link_libraries( ${APP_EXECUTABLE}
PRIVATE
revolta
)
Once being built, the include path -I/home/martin/git/revolta/source/librevolta is added (the source location) even though it is stated as PRIVATE in the snipped above:
PRIVATE
# Include directory used privately just to build the library itself
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
and only the ${CMAKE_INSTALL_PREFIX}/include is made public...
Additionally, the library is taken from the build tree rather than from the location where it is installed:
../../librevolta/librevolta.a
instead of
/home/martin/git/revolta/sysroot/lib/librevolta.a
Could you please advice me how to correctly set the revolta target the way it correctly uses its sources for building itself but once used elsewhere it provides the sysroot installed headers and built library from the same location (respecting the standard locations)?
HINT: I also tried to remove the revolta target from the app completely, specifying only to use the sys root (gcc option --sysroot=/home/martin/git/revolta/sysroot), it works fine correct headers and lib is used BUT once the librevolta is not built and installed, the target is not run prior to app build as the dependency is not defined then...
TL;DR: You need to do what's done here:
How to create a ProjectConfig.cmake file
I see a few issues with these CMakeLists.txt files but they aren't related to your problem, because if I understand correctly what you are trying to do here, then there is no problem and it is used as intended.
Let me clarify:
You have a library project that has it's own CMakeLists.txt, where you define the target revolta
You have an executable project that has it's own CMakeLists.txt, where you define your executable target and then you add the revolta target via add_subdirectory() and target_link_libraries(my_executable revolta)
If that's the case then this is just bad:
# Once the librevolta is built, install it to the sysroot as specified by 'install()' commands
add_custom_command( TARGET ${TARGET_LIBREVOLTA} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS --install . )
Forcing your build to automatically install this library is not the way to go, ever (you for example, need elevated privileges to build it in the first place, because of this command and that poses a security risk).
That being said what is happening is perfectly fine, because from the perspective of the my_executable's CMakeLists.txt you are still building i.e. you use the BUILD_INTERFACE. It is however something you do not want to do.
What instead you want to do is:
Create generator files for a revoltaConfig.cmake file. For that I will refer you to this tutorial:
How to create a ProjectConfig.cmake file
After you create such file, i.e. after building and installing revolta. You will (in the process) also create a revoltaConfig.cmake file. Which helps you populate the my_executable project via find_package(revolta).
The above is probably what you are interested in.
The generator expressions that you use to distinguish BUILD_INTERFACE and INSTALL_INTERFACE are mainly for header file locations (or other linked libraries). Because when you build the library the header files can have a different structure then when you install it (as you already know). And as such work perfectly fine in your CMakeLists.txt, because when you think about it:
You don't want to copy changes to your library files (into the install directory) just to test ongoing development (features/bugfixes) in your executable.
And during the build of the executable if your building another target then IT IS NOT INSTALLED but rather BEING BUILT. And you are most likely adding it as a built target.
So to sum up what would most likely happen here (using your old CMakeLists.txt) is that
The moment you start building the executable which adds the target library as a dependency via add_subdirectory you are implicitly using BUILD_INTERFACE because you are building.
If you were to then install both the executable and the library it would again use the correct install paths, i.e. you would then implicitly start using INSTALL_INTERFACE.
You could hack it without the projectConfig file using the same generator expressions by mixing them up, but I don't recommend it, because then the CMakeLists.txt wouldn't work without doing some weird steps beforehand.

cmake add-custom-target doesn't create a target

I want to build a package that contains a list of files (configuration files that my main task uses). So I am adding these in my CMakeLists.txt
add_custom_target(my-configs)
install(
FILES
file1.cfg
DESTINATION data/task
COMPONENT my-configs
EXCLUDE_FROM_ALL)
...
But when I run :
make -C ../cmake-build/linux_64_static_make_RelWithDebInfo/task/ my-configs
I get :
make: Entering directory '/development/cmake-build/linux_64_static_make_RelWithDebInfo/task'
make: *** No rule to make target 'my-configs'. Stop.
Why is that? Shouldn't the above create the target?
EDIT
This component doesn't do anything apart from copying files into the specified location. In that case, do I need a custom_target at all?
Or could I just go ahead and do cmake install? If I do install I see :
cd ../cmake-build/linux_64_static_make_RelWithDebInfo && DESTDIR=../../cmake-install/linux_64_static_make_RelWithDebInfo cmake -DCOMPONENT=my_configs -P cmake_install.cmake
-- Install configuration: "RelWithDebInfo"
-- Install component: "my_configs"
but nothing gets installed in the DESTDIR as expected - which is why I thought I needed a target so I can regenerate the cmake-build tree? Otherwise how will it know about the new component?
One use case for add_custom_target() can be like a .PHONY target in a Makefile.
You need to add dependencies to your custom target so that CMake knows how to fill the order. Here some examples:
add_custom_target(libs DEPENDS library_1 library_2 ...)
So now you have a target libs that will build your set of libraries when you specify that target to be built or some other target depends on libs to be built.
Another example:
add_custom_target(unmount_server COMMAND "umount /mnt/deployment")
This would provide a target that would unmount a server drive. Very much a non-portable operation as I have written it.

CMake TARGET_RUNTIME_DLLS is empty

I have git cloned, built (with MSVC for both Debug and Release) and then installed wxWidgets:
cmake -B build wxWidgets
cmake --build build --config <CONFIG>
cmake --install build --prefix my_install --config <CONFIG>
with <CONFIG> = Debug and <CONFIG> = Release.
Then I used the following CMake script to link against it, as suggested by the wiki:
cmake_minimum_required(VERSION 3.16)
project(Test)
add_executable(Test WIN32 Main.cpp)
# wxWidgets
SET(wxWidgets_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/my_install)
find_package(wxWidgets COMPONENTS core base REQUIRED)
include(${wxWidgets_USE_FILE})
target_link_libraries(Test PRIVATE ${wxWidgets_LIBRARIES})
# Copy runtime DLLs to the directory of the executable.
add_custom_command(TARGET Test POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Runtime Dlls: $<TARGET_RUNTIME_DLLS:Test>"
)
My goal is to automatically copy the DLLs into the directory of the built executable, so that they can be found at runtime. For that I'm using the TARGET_RUNTIME_DLLS generator expression (follwing the sample code in the docs). In the code above, I only print out the expression at build time for testing purposes. The problem is that it is empty.
The approach worked for me before when installing and linking SDL, but SDL provides package configuration files which create imported targets, defining the DLL location(s) via IMPORTED_LOCATION_RELEASE or IMPORTED_LOCATION_DEBUG. For wxWidgets one is apparently supposed to use the FindwxWidgets.cmake script shipped with CMake, which sadly doesn't define the produced binaries. Maybe that's why TARGET_RUNTIME_DLLS isn't populated.
Does anyone know, either how to get TARGET_RUNTIME_DLLS filled or how to obtain the list of built wxWidgets DLLs for the current configuration (Release/Debug) post build copying?
Thanks a lot in advance!
I am dealing with a similar problem.
First sanity checks:
You have to work on windows platform otherwise this feature does not
work.
Your Cmake is 3.21 or above
Next comes fuzzy part. I think the library that you are trying to include have to be a Shared Imported library and you have to set a set_target_properties for IMPORTED_IMPLIB which is a path to a .lib file of sort (dll import library, I think it is called) So you have to make sure that it is all set in the package library that you trying to link with your executable.
If you have those dll avaiable and you just want to use them and not actually build them then you can write your own cmake script that will do just what I said above. Then you can include that cmake file in your project and then link against your app.
Note: I also work on similar issue right now and what I just said have not been working very reliably. I got some dlls to be copied and some do not.
Edit:
Cmake docs give a more detailed explanation on how this library setting should look like if you use find_package feature.
Found here: https://cmake.org/cmake/help/latest/command/add_library.html#imported-libraries
An UNKNOWN library type is typically only used in the implementation
of Find Modules. It allows the path to an imported library (often
found using the find_library() command) to be used without having to
know what type of library it is. This is especially useful on Windows
where a static library and a DLL's import library both have the same
file extension.

CMake library file missing

I'm currently struggling with cmake.
I'm using Cmake for an embedded platform with GCC.
My project is separate into several modules. Each module is build into a static library. At link time, all of these libraries are collected and linked into one binary.
The problem: I created a new folder for some unit tests. All sources are build into a library libunit_tests.a.(I checked the library actually gets created).
However in my linker call other libraries are passed to the linker, mine however gets omitted resulting in an undefined reference error.
My folder structure looks like this
*
unit_tests/
*
unit_tests/inc
*unit_tests/src
There is one Cmake file located at
- /unit_tests/CMakeLists.txt
My actual CMakeLists.txt file is pretty basic
include_directories("./inc")
set(module_name "unit_tests")
set(MODULE_SOURCES
./inc/active_tests.h
./inc/Run_All_Tests.h ./src/Run_All_Tests.c
)
###########################
# add library
###########################
if(MODULE_SOURCES)
# add files to library
add_library("${module_name}"
${MODULE_SOURCES})
target_link_libraries("${module_name}"
-Wl,--start-group
-Wl,--end-group)
endif()
How do i pass this library to the linker to resolve the undefined reference error?
I thought this is done via add_libary and target_link_libraries?

How to build library or executable binary project with dependent library project with cmake

I have two cmake projects:
BiosPatcher\CommonBase\
BiosPatcher\Bios\
First one builds a library and has ./include folder with headers.
How to build second project library(or executable) which depends on first library ./include for compilation and libCommonBase.dll for execution(in case of executable file building).
I've included header files like that
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../CommonBase/include)
And it works.
And I link library like that:
target_link_libraries (Bios libCommonBase)
But I get error message:
c:\Users\Sakhno\workspace\BiosPatcher\Bios\build>make
[ 16%] Linking CXX shared library libBios.dll
C:/mingw-w64/mingw32/bin/../lib/gcc/i686-w64-mingw32/5.3.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot find -llibCommonBase
collect2.exe: error: ld returned 1 exit status
I think i need somehow specify folder to look for the library but i don't know how.
I was able to build Bios project which depends on CommonBase project target library by adding following lines.
set(COMMON_BASE_PROJECT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../CommonBase)
...
include_directories(${COMMON_BASE_PROJECT_DIR}/include)
...
target_link_libraries (Bios ${COMMON_BASE_PROJECT_DIR}/build/libCommonBase.dll)