Include header of CMake targets in a object library - c++

Scenario:
I'm porting an applications build system to CMake. Dependencies are already installed on the system or will be handled via conan.
Lets say the project is structured as following:
- include/
- BigFoo/
- *.hpp
- src/
- BigFoo/
- *.cpp
- main.cpp
- *.qml
- test/
- BigFoo/
- *Test.cpp
My goal is to reuse the compilation output (*.o) of BigFoo in the main application and the unit tests. I don't want an internal library (*.a or *.so) for BigFoo.
BigFoo is using Qt5 and Boost components from the target system. It also uses components provided as conan packages.
Current approach:
Build BigFoo as static library => handle dependencies via target_link_library()
Build main.cpp and *.qml => link with BigFoo
Build BigFoo/*Test.cpp => link with BigFoo
Desired approach:
Build BigFoo as object library => include dependency headers
Build main.cpp, *.qml with BigFoo_Object => link dependencies
Build BigFoo/*Test.cpp with BigFoo_Object => link dependencies
Problem of the desired approach:
I could not include dependency headers of several components (Boost::XYZ Qt5::XYZ CONAN_PKG::XYZ etc) via target_include_directories() when building an OBJECT library.
add_library(Core OBJECT)
# I want to do something like this:
target_include_directories(Core PUBLIC
Qt5::Core
Qt5::Qml
Qt5::Quick
Qt5::Scxml
CONAN_PKG::XYZ
CONAN_PKG::XYZ
)
The result is CMake (of course) adds header include paths like the following...
-I../../../Application/Qt5::Qml
And fails later because it cannot dismantle Qt include statements.
When building a static lib the header include was handled by target_link_libraries(), I'm now a bit confussed how to do this on a OBJECT library where I do not link anything.
My Questions:
How can I include headers of CMake targets provided by find_package() and CONAN_PKG in a OBJECT library target?
Is there is a better/nicer way of reusing BigFoo so i don't have duplicated compilation?
PS: I'm using cmake 3.13

I know that this seems nonsense, because object libraries aren't linked at all. But that is only one of the awkwardness of the CMake object libraries. This is what you probably need:
add_library(Core OBJECT)
target_link_libraries(Core PUBLIC
Qt5::Core
Qt5::Qml
Qt5::Quick
Qt5::Scxml
CONAN_PKG::XYZ
CONAN_PKG::XYZ
)
Object libraries may be used as the target (first) argument of target_link_libraries to specify dependencies of their sources on other libraries.

Related

How can I write a CMakeList.txt file for heavily nested project?

Suppose my source code has the following directory structure:
C:\USERS\PC\SOURCE\REPOS\my_app_src
├───apps {a.hh, b.cc, c.hh, d.cc}
│ └───biosimulations {main1.hh, main1.cc, x.hh, y.cc}
└───core {w.cc, x.hh, y.hh, z.cc}
└───algorithms {p.hh, p.cc, q.hh, r.cc, s.hh, s.cc}
└───trees {r.hh, r.cc, main2.hh, main2.cc}
Each folder has any number of header and source files with any name.
How can I write a CMakeList.txt file for this project?
This is scientific software.
I need to be able to use various parts of the same library to compile and build executables for multiple applications.
For example, in the above sample, main1.exe and main2.exe are supposed to be two different executable files.
Sometimes, I need to be able to switch off one or another executable from compiling.
Do you want the project to support testing, installation, and/or packaging?
No, I don't need them. I just need to be able to compile and execute the apps.
What is in core?
Model classes. e.g., Atom, Protein, Chain, etc.
Are the source files for core part of a single library or executable?
Part of executable. There is no static or dynamic library in the project.
Using add_subdirectory and add_library commands, recursively add all of the source files in the directory structure.
cmake_minimum_required(VERSION 3.x)
project(my_app)
# Add subdirectories recursively
add_subdirectory(apps)
add_subdirectory(core)
add_subdirectory(simulations)
add_subdirectory(ui)
add_subdirectory(utils)
# Create the final executable
add_executable(my_app main.cpp)
# Link the libraries to the executable
target_link_libraries(my_app core simulations apps ui utils)
Then in each subdirectories(apps,core,simulations,ui, utils) you would need to add a new CMakeLists.txt that tells which source files are in that directory and create a library.
cmake_minimum_required(VERSION 3.x)
SET(SRC_FILES
program.cpp
)
#for static libraries
add_library(core STATIC ${SRC_FILES})
#for dynamically linking libraries
add_library(core SHARED ${SRC_FILES})
# If you need executable here
# add_executable(core ${SRC_FILES})
This needs to be repeated for all subdirectories, untill all sources are covered. Above examples gives general structure, you need to define CMAKE flags as required.

Exporting and packaging prebuilt libraries in cmake

instead of asking a question directly, I'll expose my use case and the way I tried (but failed) to solve it.
Say I have:
3 shared libraries A, B and C
A require B and C
A comes with a set of headers
That's it, no extra information, it's provided by a vendor and not possibly subject to any change (modernization/cmake packages, etc).
A should always be packaged with B and C. I should only need to link with A and cmake should transitively link with B and C.
Now, I'd like to make it more "modern cmake" friendly and by able to:
First usecase: Create a repo containing these libs and calling add_subdirectory() from a parent project.
First usecase: Create a package (say debian pkg . deb) containing the relevant AConfig.cmake AConfigVersion.cmake and ATargets.cmake. Then a simple system install of the pkg and a find_package() should to the trick.
What has been done:
I tried using INTERFACE IMPORTED library and INTERFACE.
Because I want to support packaging the libs INTERFACE IMPORTED can't be used (you can't install it as far as I know/tested).
INTERFACE is working fine for the first usecase, using add_subdirectory(), headers are found, everything links, but because the user may not have at this point, the shared lib in is path, he can't run the tests for instance.
Then comes the export part needed to make the shared libs available and to make find_package() work. I succeed to export/package the libs A B C, the headers for A and the files needed for find_package().
But when in a client library D, find_package(A REQUIRED) finds the lib (no messages such as "Could not find a package configuration file provided by "A" ") it doess NOT create any target I can link on. I took a look at the generated ATargets.cmake:
# generated stuff before
add_library(A::A INTERFACE IMPORTED) # This is cannot be used at all, does not create a target I can link on unless I remove IMPORTED
set_target_properties(A::A PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include" # that's fine
INTERFACE_LINK_LIBRARIES "A.so;B.so.1.0;C.so.1.0" # that's not fine I need ${_IMPORT_PREFIX}/${CMAKE_INSTALL_LIBDIR}/ before each libs like it did for the headers
)
# generated stuff after
Note that if I remove the IMPORTED in the add_library of the ATargets.cmake and remove the namespace stuff, the target A is correctly accessible in any client cmake project using find_package but it'll NOT link correctly probably because A.so (and B and C) is not referenced using ${_IMPORT_PREFIX}/lib/A.so. I tried adding ${_IMPORT_PREFIX}/lib/ before all libs in INTERFACE_LINK_LIBRARIES, removed the IMPORTED keyword and namespace stuff and guess what, it works perfectly... Now, how do I do that without the trick.
The content of AConfig.cmake.in is:
#PACKAGE_INIT#
include(CMakeFindDependencyMacro)
# Add the targets file
include("${CMAKE_CURRENT_LIST_DIR}/ATargets.cmake")
# check_required_components(#PROJECT_NAME#) # It is commented because unless I apply the fix specified earlier (IMPORTED and namespace removed), during the find_package call I get:
#################
# CMake Error at /usr/lib/cmake/A/AConfig.cmake:8 # (check_required_components):
# Unknown CMake command "check_required_components".
#################
That's pretty much it, the rest of this post is implementation details. This code below can be used to solve use case 1 but the export package and cmake config/target files are not correct.
add_library(A
INTERFACE)
add_library(A::A ALIAS A)
target_include_directories(A
INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include>")
target_link_libraries(A
INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib/A.so>"
"$<INSTALL_INTERFACE:A.so>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib/B.so>"
"$<INSTALL_INTERFACE:B.so>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib/C.so>"
"$<INSTALL_INTERFACE:C.so>")
#### install
install(TARGETS A
EXPORT ATargets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT A_Runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT A_Runtime NAMELINK_COMPONENT A_Development
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT A_Development)
install(DIRECTORY "lib/"
DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include"
DESTINATION "include")
write_basic_package_version_file(AConfigVersion.cmake
VERSION "${PACKAGE_VERSION}"
COMPATIBILITY SameMajorVersion)
install(EXPORT ATargets
FILE ATargets.cmake
NAMESPACE A::
DESTINATION "lib/cmake/A")
configure_file(AConfig.cmake.in AConfig.cmake #ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/AConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/AConfigVersion.cmake"
DESTINATION "lib/cmake/A")
# Then some cpack stuff that is not affecting the work done earlier
In a client lib/cmake project after the installation of the generated package (generated by cpack):
find_package(A)
target_link_libraries(my_project PUBLIC/PRIVATE A::A) # Should bring in the headers and link with A B and C
Documentation:
cmake: create a new library target which consists of a prebuilt library
https://gitlab.kitware.com/cmake/community/-/wikis/doc/tutorials/Exporting-and-Importing-Targets
Possible to add an imported library to target_link_libraries that takes care of include directories too?
Exporting an imported library
https://discourse.cmake.org/t/how-to-control-import-prefix-in-exported-targets-cmake-list-file-generated-by-cmake/2291
Create relocatable package with proper autogenerated config cmake
https://discourse.cmake.org/t/exporting-packages-with-a-custom-find-module/3820/2
Thanks for reading/helping

Linking two separate cmake projects where one is dependant on the other

I have two separate projects, A and B. Both have large folder structures, lots of dependencies and both have their own CMakelists.txt structures as well. Neither depend on each other.
I am adding new functionality to project A that depends on a header from project B, along with the network of files in Project B that header depends on.
I am at a loss for the correct way to link these with cmake. I added to header as an include where necessary in my code, but am not sure the correct way to link it. Do I use target_include_directories and add the binaries from project B, or the source code, or both? Project A won't be building B as it is already set up, with the binaries and source in separate subdirectories. I just need A to be able to call the functionality from B.
I am not very familiar with cmake yet, so any pointers would be appreciated.
Thanks.
Project B should export its targets.
That way, in project A, you could do something like this:
find_package(B REQUIRED)
target_link_libraries(A PUBLIC B::B)
And when invoking CMake, add B's build directory in the prefix path:
# in A/build
cmake .. -DCMAKE_PREFIX_PATH=/path/to/B/build
But in order for that to work, you must change your B CMake files to export your targets (as it should):
include(GNUInstallDirs)
add_library(B INTERFACE)
add_library(B::B ALIAS B)
target_link_libraries(B PUBLIC dependency-of-B)
target_compile_features(B INTERFACE cxx_std_17) # or any language version
# Set B include directory. Will add include directory for both B and A
target_include_directories(B PUBLIC
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/your-include-dir>
)
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/cmake/B-config.cmake" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
install(TARGETS B EXPORT B-targets)
# install package
install(EXPORT B-targets
FILE B-targets.cmake
NAMESPACE B::
DESTINATION share/cmake/B
)
# export build tree
export(
EXPORT B-targets
FILE "${CMAKE_CURRENT_BINARY_DIR}/B-targets.cmake"
NAMESPACE B::
)
With B-config.cmake:
include(CMakeFindDependencyMacro)
# All find_package we used in CMakeLists.txt should also be there
find_dependency(dependency1 REQUIRED)
find_dependency(dependency2 REQUIRED)
include("${CMAKE_CURRENT_LIST_DIR}/B-targets.cmake")
If you have many target in B, you should install(TARGETS other-target EXPORT B-targets)
I strongly recommend looking at example of libraries that export their targets so you can base your work on it. For example, the glm library export its targets and always worked well. I also added target exportation in msdfgen project.
If you only want to use headers and not object files then just target_include_directories is all you need. Dependency tracking will be done by cmake and ninja/make automatically.
Alternatively, if you need to include both headers and objects from B, you'd really want to factor out these bits into small stand-alone static libraries within B, and then link with those libraries from A - this will add include file paths and everything else needed. cmake is quite good at it.

Cannot link local libraries in CMake

I'm developing a c++ program on visual studio that will be deployed on linux, and it is debugged on linux through an ssh. Currently, this is the structure of my folder:
ANT
-xscommon
--xscommon_config.h
-xscontroller
-xstypes
ANT.cpp
ANT.h
CMakeLists.txt
CMakeSettings.json
hashes.h
quaternionic.h
stars.h
Currently, all the .h, .cpp, .o, .cpp.o, .a files that I think I have to link to are kept within the three xs------- directories. I am quite new to cmake, and this linking to these libraries is giving me trouble; I am able to link correctly to the includes, but there are undefined references errors thrown when I don't do linking, and when I attempt linking, it throws errors. This is my current CMakeLists.txt file:
# CMakeList.txt : CMake project for ANT, include source and define
# project specific logic h"ere.
#
cmake_minimum_required (VERSION 3.8)
project ("ANT")
link_directories(${ANT_SOURCE_DIR}/xscommon xscontroller xstypes)
add_executable(
ANT
"ANT.cpp"
"ANT.h"
"quaternionic.h"
"stars.h"
"hashes.h"
)
target_include_directories(ANT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(ANT PUBLIC xscommon_config)
When I run this, the builder says the following:
/usr/bin/ld: cannot find -lxscommon_config
I need to look for these libraries in the directory that ANT.cpp is in, as this is where they are kept, however nothing I do (and I have messed around with configurations for hours now) will tell camke to look for these libraries in the src folder. it always goes to /usr/bin/ld.
I really just need to know what to tell CMake such that it will look in the correct place for each file, that is if I am telling it to look for the correct file (I am fairly sure I am).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Update
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
So I have remade the CMakeLists.txt file to this:
# CMakeList.txt : CMake project for ANT, include source and define
# project specific logic here.
#
cmake_minimum_required (VERSION 3.15)
project ("ANT")
#[STATIC | SHARED | MODULE]
#[STATIC | SHARED | MODULE]
#[STATIC | SHARED | MODULE]
add_library(xscommon SHARED IMPORTED)
add_library(xscontroller SHARED IMPORTED)
add_library(xstypes SHARED IMPORTED)
add_executable(
ANT
"ANT.cpp"
"ANT.h"
)
target_include_directories(ANT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
And still get undefined references. I am going to try building the libraries instead. Additionally, I have contacted the manufacturer of the IMUs which use this SDK, as colleagues have not been able to fix this either.
The problem is you are linking to a library that has not been build.
This
# link to this directory
target_link_libraries(ANT PRIVATE xscommon)
tries to link to a library called xscommon to the target ANT but you have not build xscommon anywhere in your project.
If xscommon is a pre-build library and you just want to import it then add the library and set the IMPORTED target property:
add_library(xscommon [STATIC | SHARED | MODULE] IMPORTED)
If you want to build xscommon in your root CMakeLists.txt. Add xscommon as a library and include the location of the headers.
add_library(xscommon [STATIC | SHARED | MODULE]
xxx/xxx.cpp #list all source files that build the library - use relative path
)
target_include_directories(xscommon PRIVATE
xxx/xxx #path to the location of library header files
)
Also you don't need to add the header files when adding the executable. So this
add_executable(
ANT
"ANT.cpp"
"ANT.h"
"quaternionic.h"
"stars.h"
"hashes.h"
)
can be simplified to
add_executable(
ANT
"ANT.cpp"
)
Suppose your dir is like this:
ANT
-xscommon
--xscommon_config.h
--xscommon_config.cpp
...
First add a CMakeLists.txt file to xscommon/:
ANT
-xscommon
--CMakeLists.txt
--xscommon_config.h
--xscommon_config.cpp
...
Now in xscommon/CMakeLists.txt we will create a library, that will be imported and linked in the main CMakeLists.txt file:
xscommon/CMakeLists.txt:
#define another target, let's name it 'xscommon'
add_library(xscommon
xscommon_config.h
xscommon_config.cpp
#more sources if you want
)
Now in the main CMakeLists.txt file:
cmake_minimum_required (VERSION 3.8)
project ("ANT")
#remove this line
#link_directories(${ANT_SOURCE_DIR}/xscommon xscontroller xstypes)
add_executable(
ANT
"ANT.cpp"
"ANT.h"
"quaternionic.h"
"stars.h"
"hashes.h"
)
# add the xscommon directory, this will make the library target defined there available here
add_subdirectory(xscommon)
# link to this directory
target_link_libraries(ANT PRIVATE xscommon)
# use PUBLIC if the xscommon library will be part of the public interface of your
# library. But since it is an executable, PRIVATE is better here.
target_include_directories(ANT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Use the above method, you can create more libraries and link to them.
Note that it is not necessary to create separate cmake files for each subdirectory but it is considered a good practice and modularizes your code. If you want to do this in the main cmake file instead of creating a subdirectory, add this to the main cmake:
add_library(xscommon
xscommon/xscommon_config.h
xscommon/xscommon_config.cpp
#more sources if you want
)
Update according to the changes in question
Your current CMakeLists.txt:
These three lines below are not doing anything, definitely not what you think. add_library() command has the word "add" in it, I know, but it doesn't add any library, just like add_executable doesn't add any executable. It creates a library.
add_library(xscommon SHARED IMPORTED)
add_library(xscontroller SHARED IMPORTED)
add_library(xstypes SHARED IMPORTED)
How to create a library in cmake out of two file a.cpp and a.h:
add_library(myALib "a.cpp")
That's it. If you have more sources, you will include them accordingly of course. In your case you will have to add the sources of xscommon and others accordingly.
Once you have created the libraries, you need to link them to your executable. If you won't you will get undefined reference errors because compiler can locate the declarations in header files but not the definitions of your code which exists in .cpp files.
So, how do you link? Simple:
target_link_libraries(TARGET_NAME PUBLIC | PRIVATE LIBRARY_NAME)
# TARGET_NAME: can be `executable` or `library`
# PUBLIC or PRIVATE (for exe, it is usually private)
# LIBRARY_NAME: Name of library you want to link to TARGET_NAME
# So if you wanted to link "myALib" which I created above to ANT, you would do:
target_link_libraries(ANT PRIVATE myALib)
# Note: You need to add this line **after** add_executable() because target "ANT" will be created after that. You can do the linking after the "target_include_directories" command.

CMake - objects being built twice unnecessarily?

I have a list of files that need to be compiled for my main executable. My tests also need these files. When the test executable(s) are built, the object files are built again, even though earlier in the build they were built when the main executable was built.
Am I wrong in thinking this is not needed? If so is there a way to disable this?
Example:
set(SOURCES
${SOURCE_DIR}/file.c
${SOURCE_DIR}/another_file.c)
set(MAIN ${SOURCE_DIR}/main.c)
add_executable(main_executable ${SOURCES} ${MAIN})
add_executable(test1_ex ${PROJECT_SOURCE_DIR}/test/test1.cc ${SOURCES})
Put the common code in a library and link the library to both your application and tests.
# The application's sources - except main
set(SOURCES
${SOURCE_DIR}/file.c
${SOURCE_DIR}/another_file.c)
# build an application library
add_library(app_lib ${SOURCES})
# build an executable in terms of the application library
set(MAIN ${SOURCE_DIR}/main.c)
add_executable(main_executable ${MAIN})
target_link_libraries(main_executable PRIVATE app_lib)
# build a test executable in terms of the application library
add_executable(test1_ex ${PROJECT_SOURCE_DIR}/test/test1.cc)
target_link_libraries(test1_ex PRIVATE app_lib)
This could be due to the following scenario:
add_library(mylib1 file1.cpp common.cpp)
add_library(mylib2 file2.cpp common.cpp)
That created the same problem you're having for me, where common.cpp was being rebuilt for every library. My solution was this:
add_library(common_lib common.cpp)
add_library(mylib1 file1.cpp)
add_library(mylib2 file2.cpp)
target_link_libraries(mylib1 common_lib)
target_link_libraries(mylib2 common_lib)
That resolved the problem for me as the building (object creation) was done once, but it was linked to every other library.