extract header file exported Target (Shared Library) using CMake - c++

Background
I am trying to use the export/import feature of CMake.
My project structure as like as follows
cmake_export_import
|-- child_project
| |-- CMakeLists.txt
| |-- include
| | `-- child.h
| `-- src
| |-- child.cpp
| `-- main.cpp
`-- parent_project
|-- CMakeLists.txt
|-- include
| `-- parent.h
`-- src
|-- main.cpp
`-- parent.cpp
I want to use the property of parent_project into child_project
I will not use add_subdirectory in child_project to add the parent project Also I will not do make install during building parent_project. This is my limitation or requirements, whatever you say.
Approach
Building parent proj
I have followed this to achieve this.
CMakeLists.txt from parent_project
cmake_minimum_required(VERSION 3.10)
project(parent)
# Set the main target name to the project name
set(MAIN_TARGET_NAME ${PROJECT_NAME})
# executable file path
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../bin)
# Create the C++ shared library
add_library(${MAIN_TARGET_NAME}_lib SHARED ${${PROJECT_NAME}_SOURCE_DIR}/src/parent.cpp)
# Targetting header files for the target ${MAIN_TARGET_NAME}_lib
target_include_directories(${MAIN_TARGET_NAME}_lib PUBLIC ${PROJECT_SOURCE_DIR}/include)
add_executable(${MAIN_TARGET_NAME}_executable ${${PROJECT_NAME}_SOURCE_DIR}/src/main.cpp)
# Set the name of the binary file and the output directory
set_target_properties(${MAIN_TARGET_NAME}_executable PROPERTIES OUTPUT_NAME "parent_executable")
set_target_properties(${MAIN_TARGET_NAME}_executable PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${${PROJECT_NAME}_SOURCE_DIR}/bin)
# Set the name of the lib and the output directory
set_target_properties(${MAIN_TARGET_NAME}_lib PROPERTIES RELEASE_OUTPUT_NAME "parent_library")
set_target_properties(${MAIN_TARGET_NAME}_lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${${PROJECT_NAME}_SOURCE_DIR}/lib)
target_link_libraries(${MAIN_TARGET_NAME}_executable ${MAIN_TARGET_NAME}_lib)
target_include_directories(${MAIN_TARGET_NAME}_lib PUBLIC
${PROJECT_SOURCE_DIR}/include)
install(TARGETS ${MAIN_TARGET_NAME}_lib
EXPORT parent_Targets
)
# # exporting target from build tree
export(EXPORT parent_Targets
FILE "${CMAKE_CURRENT_BINARY_DIR}/../cmake_export/parent_Targets.cmake"
NAMESPACE cmake_exp_imp_parent::
)
I have build the parent project
cd parent_project
mkdir build && cd build
cmake .. && make
Then I can see in the root of parent project a folder with a generated .cmake file cmake_export/parent_Targets.cmake
Building child proj
CMakeLists.txt from child_project
cmake_minimum_required(VERSION 3.10)
project(child)
# Set the main target name to the project name
set(MAIN_TARGET_NAME ${PROJECT_NAME})
# executable file path
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../bin)
# including the generated cmake of parent proj
include(${CMAKE_BINARY_DIR}/../../parent_project/cmake_export/parent_Targets.cmake)
# get the include header property
get_target_property(PARENT_INCLUDES cmake_exp_imp_parent::parent_lib INTERFACE_INCLUDE_DIRECTORIES)
add_library(${MAIN_TARGET_NAME}_lib SHARED ${${PROJECT_NAME}_SOURCE_DIR}/src/child.cpp)
# Targetting header files for the target ${MAIN_TARGET_NAME}_lib
target_include_directories(${MAIN_TARGET_NAME}_lib PUBLIC ${PROJECT_SOURCE_DIR}/include
${PARENT_INCLUDES})
add_executable(${MAIN_TARGET_NAME}_executable ${${PROJECT_NAME}_SOURCE_DIR}/src/main.cpp)
# Set the name of the binary file and the output directory
set_target_properties(${MAIN_TARGET_NAME}_executable PROPERTIES OUTPUT_NAME "child_executable")
set_target_properties(${MAIN_TARGET_NAME}_executable PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${${PROJECT_NAME}_SOURCE_DIR}/bin)
# Set the name of the lib and the output directory
set_target_properties(${MAIN_TARGET_NAME}_lib PROPERTIES RELEASE_OUTPUT_NAME "child_library")
set_target_properties(${MAIN_TARGET_NAME}_lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${${PROJECT_NAME}_SOURCE_DIR}/lib)
target_link_libraries(${MAIN_TARGET_NAME}_executable ${MAIN_TARGET_NAME}_lib
cmake_exp_imp_parent::parent_lib)
I have build the child project
cd child_project
mkdir build && cd build
cmake .. && make
Project is perfectly build.
But i am not satisfied here. Maybe I am decoding the CMake documentation in a wrong way.
Want to know
I have used the following lines i child project CMake file to get the header file of the parent projet. But truly I don't want to use this rather I am inclined to fetch automatically the path from the exported target by importing.
# including the generated cmake of parent proj
include(${CMAKE_BINARY_DIR}/../../parent_project/cmake_export/parent_Targets.cmake)
# get the include header property
get_target_property(PARENT_INCLUDES cmake_exp_imp_parent::parent_lib INTERFACE_INCLUDE_DIRECTORIES)
And then I have appended the variable ${PARENT_INCLUDES}
target_include_directories(${MAIN_TARGET_NAME}_lib PUBLIC ${PROJECT_SOURCE_DIR}/include
${PARENT_INCLUDES})
This helped me to find the header file of parent project.
But my query is as parent project's target is exported so is not is also exported it's all dependencies (I mean the header file path)?
One snipet from the generated cmake file
set_target_properties(cmake_exp_imp_parent::parent_lib PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "Users/cmake_export_import/parent_project/include"
)
Here, i can see that the header file path of the parent project is added. But, how do I use it without using get_target_property? I have found here abpout INTERFACE_INCLUDE_DIRECTORIES and thinking somehow it is available while I use target_link_libraries to link the exported target.
Eg:
target_link_libraries(${MAIN_TARGET_NAME}_executable ${MAIN_TARGET_NAME}_lib
cmake_exp_imp_parent::parent_lib)
Any idea is highly appreciable.

Related

How to emulate find_package() to do same as add_subdirectory()? (Modern CMake)

My question is greatly related to what they call Modern CMake. I've read a lot about CMake trying to get the best practice to avoid anti-patterns.
My project is under development and I don't want to install my library nor having anything being generated outside the repo. ($HOME, /usr/local or wherever)
Everything must stay inside the repository.
I read add_subdirectories() using out-of-tree folder is an anti-pattern. I'm looking for a better way of doing what I want.
I have a library and an app under development, each in their own folder. During the development phase, the library shouldn't be treated as a library (.a/.so).
When I compile the app, I'd like the lib to be built too.
.
+-- lib
| +- CMakeLists.txt
| +- src
| | +- lib.cpp
| +- include
| | +- lib.h
+-- app
+- CMakeLists.txt
+- src
| +- app.cpp
+- include
| +- app.h
lib/CMakeLists.txt:
add_library(mylib src/lib.cpp)
# Export the public header files to any future user
target_include_directories(mylib
PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include
)
app/CMakeLists.txt: I was thinking using find_package() with a cmake/FindMyLib.cmake
cmake/FindMyLib.cmake should contain the link to ../lib, in the same way I'd do with add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../lib build)
list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake)
find_package(mylib REQUIRED)
include_directories("${mylib_INCLUDE_DIRS}")
Is my workflow legit?
How can I do it?
Why isn't it easier to do such a simple project.. CMake is just a jungle.
Few notes concerning your "Modern" CMake.
Your include calls are wrong please use:
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/mylib>
$<INSTALL_INTERFACE:include/mylib> # <prefix>/include/mylib
)
i.e. you don't want to see a local path in the install export.
ref: https://cmake.org/cmake/help/latest/command/target_include_directories.html
And since you are using target properties, you won't need any mylib_INCLUDE_DIRS, simply use:
target_link_libraries(myapp PRIVATE mylib)
here mylib will "forward" its include directories to your app.
You could of course add a CMakeLists.txt file to the parent directory of lib and app
project(SomeName)
add_subdirectory(lib)
add_subdirectory(app)
To me this seems like the only option to have both mylib and app available as targets in the same project. This allows you to simply use
target_link_libraries(app PRIVATE mylib)
in app/CMakeLists.txt.
However there's no need to install the library outside of the repository. Just specify CMAKE_INSTALL_PREFIX to be a directory inside the repository and specify a seperate directory for packaging by setting CPACK_PACKAGING_INSTALL_PREFIX. In fact this is what cpack does prior to adding all the files to the output file.
lib/CMakeLists.txt
add_library(mylib src/lib.cpp include/lib.h) # added header here to show up in IDE
# Export the public header files to any future user
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/mylib>
$<INSTALL_INTERFACE:include>
)
set_target_properties(mylib PROPERTIES PUBLIC_HEADER mylib/include/lib.h)
install(TARGETS mylib EXPORT mylib
ARCHIVE DESTINATION lib # .lib destination
LIBRARY DESTINATION lib # .so location
RUNTIME DESTINATION bin # .dll location
PUBLIC_HEADER DESTINATION include
)
install(EXPORT mylib DESTINATION mylib FILE mylib-config.cmake)
You could build and install the library to a directory inside the repository by executing the following commands starting at the parent directory of lib and app (assuming linux bash here):
mkdir mylib/build
mkdir local_installs
cmake -D "CMAKE_INSTALL_PREFIX:PATH=$(pwd)/local_installs" -S mylib -B mylib/build
cmake --build mylib/build --target INSTALL
This allows you to use find_package using the config version in app:
find_package(mylib REQUIRED
PATHS "${CMAKE_CURRENT_SOURCE_DIR}/../local_installs/mylib"
NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH # prevent lookup at location other than local_installs first
)

Link up src/ with CMake as library #include'd with some `libname/` prefix

For my research project I am setting up a project (coom) to benchmark a set of algorithms on a data structure. For unit testing I settled on Bandit, which leaves me with a project structure that looks as follows:
+ root
|-- CMakeLists.txt
|-+ external/
| \-- bandit/
|-+ src/
| |-- CMakeLists.txt
| |-- node.cpp
| \-- node.h
\-+ test/
|-- CMakeLists.txt
|-- test.cpp
\-- test_node.cpp
From my experience with other languages, this seems to me a standard project structure? The test/ folder contains unit tests for the logic in src/ and no dependencies are intermixed with the source and test code, but are instead in external/.
The testing files I want to look as follows (with irrelevant parts removed)
// test/test.cpp
#include <bandit/bandit.h>
(...)
#include "test_node.cpp"
int main(int argc, char* argv[]) {
(...)
}
// test/test_node.cpp
#include <coom/node.h>
(...)
But my problem is, that when I try to compile with cmake .. and the subsequent Makefile, they are unable to find the source code in src/ where I get the compiler error:
fatal error: coom/node.h: No such file or directory.
I would expect the test/CMakeLists.txt should look somewhat like the following:
# test/CMakeLists.txt
add_executable (test_unit test.cpp)
target_link_libraries(test_unit coom)
I cannot figure out how to setup the CMakeLists.txt and src/CMakeLists.txt to ensure I get the desired outcome above. Currently they look as follows:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project (coom VERSION 0.1)
# ============================================================================ #
# Dependencies
(...)
# ============================================================================ #
# COOM project
add_subdirectory (src)
add_subdirectory (test)
# src/CMakeLists.txt
# ============================================================================ #
# Link up files for the library
set(HEADERS
node.h
)
set(SOURCES
node.cpp
)
add_library(coom ${HEADERS} ${SOURCES})
I can see from other projects, that it is possible to link the src/ directory with some libname/ prefix, but I cannot discern from their CMakeLists.txt files what I am doing wrong. I have looked at writing a coom.pc.in file and providing an install-target, and tried to set_target_properties with either FOLDER coom or PREFIX coom, but neither worked. I can hack an include_directory(../src) into the test/CMakeLists.txt to be able to include the file via an #include <node.cpp>, but that screams I'm doing something inherently wrong.
I'm at this point very much pulling my hairs out, and the CMake documentation is of very little help to me.
Your coom target has no include directories defined. You can define the include directories to use for this target (with target_include_directories()), and propagate these include directories so they are visible to the consuming test_unit target (by using PUBLIC):
# src/CMakeLists.txt
# ============================================================================ #
# Link up files for the library
set(HEADERS
node.h
)
set(SOURCES
node.cpp
)
add_library(coom ${HEADERS} ${SOURCES})
target_include_directories(coom PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Furthermore, the file path to the node.h header is coom/src/node.h, not coom/node.h. But, because you now have coom/src as an public include directory, you can use the following to include the node.h header in your test file:
#include <node.h>

Library headers not visible in headers but perfectly visible inside .cpp file under cmake build. Why?

I have a C++ project containing several modules, some built as libraries, with such structure:
/MyProject
+---/build
/ModuleA
+---CMakeLists.txt <- module level CMakeLists
+---/src
| +--CMakeLists.txt <- src level CMakeLists
| +--FileA1.cpp
| +--FileA2.cpp
+---/include
| +--FileA1.h
| +--FileA2.h
| +--FileA3.h
/ModuleB
+---CMakeLists.txt
+---/src
| +--CMakeLists.txt
| +--FileB1.cpp
| +--FileB2.cpp
+---/include
| +--FileB1.h
| +--FileB2.h
| +--FileB3.h
main.cpp
CMakeLists.txt <- project level CMakeLists
CMakeLists.txt files look as follow:
project level:
cmake_minimum_required(VERSION 3.05)
project(MyProject)
subdirs(ModuleA ModuleB)
set(CMAKE_CXX_STANDARD 11)
add_executable(MyProject main.cpp)
target_link_libraries(MyProject ModuleA ModuleB)
module level:
subdirs(src)
src level:
FIND_PACKAGE(SomePackage REQUIRED)
INCLUDE_DIRECTORIES(
${SomePackage_INCLUDE_DIR}
${MyProject_SOURCE_DIR}/ModuleA/include
)
SET(SOURCE_FILES <all files from ModuleA/src goes here>)
ADD_LIBRARY(ModuleA STATIC ${SOURCE_FILES})
TARGET_LINK_LIBRARIES(ModuleA
${SomePackage_LIBRARIES}
)
The problem is: when I include header files from 'SomePackage' in my ModuleA header files (i.e. SomePackageFile.hpp in FileA1.h) I get an error while running a build with make:
fatal error: SomePackageFile.hpp: No such file or directory
When I include them in cpp files they are visible and project compiles correctly. I assume that is sth wrong with CMakeLists on src level or entire hierarchy of files is missing something.
I have a github project to be used as a skeleton for other projects:
https://github.com/gnyiri/cmake-sandbox
If you follow this layout, you will not need to add ${SomePackage_INCLUDE_DIR} to INCLUDE_DIRECTORIES which is not the best way to add directories to the include path otherwise.
In a nutshell, you should define a new library like this:
project(module_a)
set(sources
src/source_a_1.cc
)
add_library(library_a
${sources}
)
target_include_directories(library_a
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
Then, if you define another library (library_b), you only need to add library_a in target_link_libraries:
project(module_b)
# set list of sources, needs to be extended when new source arrives
set(sources
src/source_b_1.cc
)
# define a library (static by default -> liblibrary_b.a or library_a.lib will be generated)
add_library(library_b
${sources}
)
# include directories
target_include_directories(library_b
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
# link library_b
target_link_libraries(library_b
library_a
)
Note that in this source tree, all the header files are located in
<module>/include/<module>
This way you will include a header file from like this:
#include "<module>/<module_header.h>"
This is simply because /include will be on include path.
Switching from INCLUDE_DIRECTORIES() to TARGET_INCLUDE_DIRECTORIES() was the case, no change to the structure of the project was needed.

How debug dynamic library in such situation

Well, I am new to CMake.
I have the following file structure
haze_removal
|---build
|---bin
| |--Test
| |--CMakeLists.txt
|---lib
| |--libtools.so
|---include
| |--tools.hpp
|---test
| |--main.cpp
| |--CMakeLists.txt
|---src
| |--tools.cpp
| |--CMakeLists.txt
|---CMakeLists.txt
The libtools.so is builded from ../src/tools.cpp. I build the whole project in ../build using the following cmake command:
cmake -DCMAKE_BUILD_TYPE=Debug ..
make
The Test is build from ../test/main.cpp
I build this project successfully. But when I debug Test using gdb ./Test, I can't skip in the function that from libtools.so.
These are my CMakeLists.txt from different directories.
CMakeLists.txt under haze_removal/
cmake_minimum_required(VERSION 2.8)
project(haze_removal)
# find needed package
find_package(OpenCV REQUIRED)
# library directory
add_subdirectory(src)
# test
add_subdirectory(test)
CMakeLists.txt under ../src/
# generate dynamic library
# add source file, include directories
aux_source_directory(. TOOLS_SRC)
include_directories(${PROJECT_SOURCE_DIR}/include)
# generate
add_library(tools SHARED ${TOOLS_SRC})
# set output directory and lib's name
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
set_target_properties(tools PROPERTIES OUTPUT_NAME "tools")
# link library
target_link_libraries(tools ${OpenCV_LIBS})
CMakeLists.txt under ../test/
# add source file, include directories, link directories
aux_source_directory(. EXE_SRC)
include_directories(${PROJECT_SOURCE_DIR}/include)
link_directories(${PROJECT_SOURCE_DIR}/lib)
# generate
add_executable(Test ${EXE_SRC})
# set output directory
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
# link libraries
target_link_libraries(Test ${OpenCV_LIBS} libtools.so)
My question is how I can debug the functions that from libtools.
Well, I find that why I can't step in the dynamic library even though I set CMAKE_BUILD_TYPE=Debug. Because before I set the build model to Debug I used to set CMAKE_BUILD_TYPE=Release. After I change the build model, I didn't delete the files in the build directory.
I don't know whether the above explanation is right or not, but I do solve my problem.
Thx!

CMake splits the generated files between build and src directory

My problem is when I run $ cmake /path/to/source/ the resulting files and directories are split between the directory I'm calling from and /path/to/source/include.
Here is my CMake project:
File structure:
root:
|-CMakeLists.txt
|-src
| |-CMakeLists.txt
| |-"source files"
|-include # This is where part of the generated files are ending up.
| |-CMakeLists.txt
| |-"include files"
Here are my CMakeLists.txt's:
root/CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
project(DUCKSIM)
# Add the root directory for the CMakeLists.txt being called. This is necessary for
# out-of-tree builds.
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
# Add the directories containing source and header files.
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/include)
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY $(pwd)) # This is an attempt to fix my problem
src/CMakeLists.txt:
# set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Set DUCKSIM_SOURCES as all .cpp files
file(GLOB DUCKSIM_SOURCES *.cpp)
# Set the name of the executable as "ducksim" and link it with main.cpp
# and every thing in the DUCKSIM_SOURCES variable.
add_executable(ducksim main.cpp ${DUCKSIM_SOURCES})
include/CMakeLists.txt:
# set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Set DUCKSIM_SOURCES as all .h files
file(GLOB DUCKSIM_SOURCES *.h)
Command add_subdirectory() will optionally accept a second path as a parameter, but that will indicate the binary directory, as indicated in the documentation. By giving your include folder as the second parameter, CMake assumes that you want the binaries to go there. That's why you end up with some files at the top level (CMakeCache.txt, etc.) and some files in the include folder.
For the record, using file(GLOB ...) is not recommended for collecting source files to compile. If you add a source file, no CMake file will have changed, and the build system won't regenerate.
Finally, you shouldn't need the file(GLOB ...) for the header files, but you probably need an include_directories() call for the include folder.