Cmake - Add files from other sub-projects - cmake

Let's say I have two sub-projects: A and B
This is my root CMakeLists.txt
cmake_minimum_required (VERSION 3.8)
project ("Project")
add_subdirectory ("A")
add_subdirectory ("B")
In /B I have file test.h
How can I include /B/test.h in A ?
I have tried to add target_include_directories(B) in /A/CMakeLists.txt but it does not seem to work

When you write add_subdirectory(B) (quotes are not necessary), you must have a CMakeLists.txt in the directory B.
This CMakeLists.txt may have the following content:
add_library(targetB INTERFACE)
target_include_directories(targetB INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
These 2 lines create a CMake target called targetB with the INTERFACE property. This is very handy for header only library. All cmake target that links with targetB will then include the CMAKE_CURRENT_SOURCE_DIR with is the directory A where your test.h is.
Then, you have add_subdirectory(A) with a CMakeLists.txt that may look like this (assuming you have a main in this dir A):
add_executable(foo ${CMAKE_CURRENT_SOURCE_DIR}/main.c)
target_link_libraries(foo PRIVATE targetB)
Then second line create the relation between the target foo and the target targetB and, when compiling, the path to your test.h will be set in the command line.

Related

INCLUDE_DIRECTORIES property of dependent target is not populated in CMake

I've read in few places, this is one of them, that when using the meaning of the PUBLIC, PRIVATE and INTERFACE keywords in the context of commands such as target_include_directories is as follows:
PRIVATE - directories after this keyword will be added to the INCLUDE_DIRECTORIES property of the target specified.
INTERFACE - directories after this keyword will be added to the INTERFACE_INCLUDE_DIRECTORIES property of the target specified.
PUBLIC - directories after this keyword will be added to both.
Directories that will be added to the INTERFACE_INCLUDE_DIRECTORIES will be added to the INCLUDE_DIRECTORIES of any target dependent on the current target.
OK I run the following experiment:
// libmymath/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
add_library(MyMath src/mymath.cpp)
target_include_directories(MyMath PUBLIC include)
get_target_property(MYMATH_INC_DIR MyMath INCLUDE_DIRECTORIES)
get_target_property(MYMATH_INC_DIR_INTERFACE MyMath INTERFACE_INCLUDE_DIRECTORIES)
message("Libmath INCLUDE_DIRECTORIES: ${MYMATH_INC_DIR}")
message("Libmath INCLUDE_DIRECTORIES INTERFACE: ${MYMATH_INC_DIR_INTERFACE}")
calculator/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
add_executable(calculator calculator.cpp)
target_link_libraries(calculator MyMath)
get_target_property(CALCULATOR_INC_DIR calculator INCLUDE_DIRECTORIES)
get_target_property(CALCULATOR_INC_DIR_INTERFACE calculator INTERFACE_INCLUDE_DIRECTORIES)
message("Calculator INCLUDE_DIRECTORIES: ${CALCULATOR_INC_DIR}")
message("Calculator INCLUDE_DIRECTORIES INTERFACE: ${CALCULATOR_INC_DIR_INTERFACE}")
// top-level CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(Math)
add_subdirectory(libmath)
add_subdirectory(calculator)
get_target_property(CALC_INC_DIR calculator INCLUDE_DIRECTORIES)
get_target_property(CALC_INC_DIR_INTERFACE calculator INTERFACE_INCLUDE_DIRECTORIES)
message("From top-level CMakeLists.txt: Calculator INCLUDE_DIRECTORIES: ${CALC_INC_DIR}")
message("From top-level CMakeLists.txt: Calculator INTERFACE_INCLUDE_DIRECTORIES: ${CALC_INC_DIR_INTERFACE}")
Running this gives:
Libmath INCLUDE_DIRECTORIES: /home/yoav/playground/cmake/math/libmath/include
Libmath INCLUDE_DIRECTORIES INTERFACE: /home/yoav/playground/cmake/math/libmath/include
Calculator INCLUDE_DIRECTORIES: CALCULATOR_INC_DIR-NOTFOUND
Calculator INCLUDE_DIRECTORIES INTERFACE: CALCULATOR_INC_DIR_INTERFACE-NOTFOUND
From top-level CMakeLists.txt: Calculator INCLUDE_DIRECTORIES: CALC_INC_DIR-NOTFOUND
From top-level CMakeLists.txt: Calculator INTERFACE_INCLUDE_DIRECTORIES: CALC_INC_DIR_INTERFACE-NOTFOUND
So we see that the INCLUDE_DIRECTORIES property of calculator is not populated, although I can see in the flags.make file of this target that the CXX_INCLUDE variable is set properly !
What gives?
Content of target property INTERFACE_INCLUDE_DIRECTORIES is actually transferred into property INCLUDE_DIRECTORIES of every target, which consumes (with target_link_libraries command) given target.
But CMake defers all transfers to a consuming target until the generation phase, which occurs after whole CMakeLists.txt has been processed. This is why one cannot obtain final value of the property using get_target_property command: this command can only obtain current value of the property, before the transferring.
However, target properties referred by the generator expressions are expanded to the final value. (But again, this expansion is observable only at the generation phase and after it).
One way to see the expanded value of a generator expression is file(GENERATE): this command will create a file, which contains expanded generator expressions.
Example:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project (hello)
add_library(foo SHARED foo.c)
target_include_directories(foo PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_library(bar SHARED bar.c)
target_include_directories(bar PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include1)
target_link_libraries(bar PUBLIC foo)
get_target_property(bar_include_directories bar INCLUDE_DIRECTORIES)
message(STATUS "bar include directories: ${bar_include_directories}")
file(GENERATE
OUTPUT bar_include_directories.txt
CONTENT "include directories: $<TARGET_PROPERTY:bar,INCLUDE_DIRECTORIES>\n"
)
When configure this CMake project, only current value of the property will be printed:
build$ cmake ..
-- bar include directories: /home/tester/tests/cmake/include1
-- Configuring done
-- Generating done
-- Build files have been written to: /home/tester/tests/cmake/build
But the file bar_include_directories.txt will contain final value of the property:
$ cat bar_include_directories.txt
include directories: /home/tester/tests/cmake/include1;/home/tester/tests/cmake
Directories that will be added to the INTERFACE_INCLUDE_DIRECTORIES will be added to the INCLUDE_DIRECTORIES of any target dependent on the current target.
I believe this is a simple way to explain it that way, but indeed this is not something that can be observed inside cmake scripts, but it's rather "generated" when cmake generates the build system files.
target_link_libraries populates LINK_LIBRARIES and INTERFACE_LINK_LIBRARIES properties of a target. Then like after cmake has run all scripts and is generating build files, it goes through LINK_LIBRARIES of a target and takes INTERFACE_INCLUDE_DIRECTORIES of dependent targets and generates build system files that include these directories for that target.

How can we refer external cmake file from main CMakeLists.txt file?

Let's say I have some protobuf related cmake code as a library that resides inside CMakeLists.pro file and I need to include this library as a external file configuration. How to do that ?
I think this question is asking how to make cmake modules. IE you have helper code and would like to make it easily available.
A normal top level project has a top level cmake folder. Here is an example of what that would look like.
cmake
tests
src
.gitignore
CMakeLists.txt
README.md
Inside the cmake folder say you have a cmake module called foo.cmake
(It's important to make the file end with .cmake file extension)
Anyway this is what your foo.cmake may look like
# Include guards need at least cmake 3.10
include_guard() # Good practice to use an include_guard()
function(bar)
...
endfunction()
Now in your main cmakelists how do you call this function? Simple.
cmake_minimum_required(VERSION 3.18)
# Add the cmake folder to the cmake module path, this makes it easier to include files
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
# Include foo.cmake
include(foo)
# Call the bar function you defined in foo.cmake
bar()
And that's how you can refer to external cmake file from the main CMakeLists.txt

Adding compiler "#define" flags in sub_directory CMakeLists.txt

My directory structure of project is build with multi CMakeLists.txts.
root
CMakeLists.txt
Src
main.c
CMSIS_lib
calculate.c
calculate.h
CMakeLists.txt
cmake
toolchain.cmake
build
In my CMSIS_lib I build separately my dependency source files calculate.c
and calculate.h with CMSIS_lib/CMakeList.txt:
set(util_source_files
calculate.c
calculate.h
)
add_library(util ${util_source_files})
target_include_directories(util calculate.h)
In my root CMakeLists.txt:
cmake_minimum_required(VERSION 3.4)
project(main_projct)
set(TOOLCHAIN_PREFIX /opt/gcc-arm-none-eabi)
set(CMAKE_TOOLCHAIN_FILE cmake/toolchain.cmake)
add_subdirectory(CMSIS_lib)
add_executable(main_projct main.c)
target_link_libraries(main_projct util)
Problem is that I must tell my compiler to add a #define GUCCI in my calculate.h (In MakeFile I know there is flag to tell a header define with -DGUCCI). I would like to add this flag to my compiler in my CMSIS_lib/CMakeList.txt, because when the first CMSIS_lib/CMakeList.txt is done building, he will skip everything under #ifndef GUCCI in my calculate.h, and when added in root CMakeLists.txt with target_link_libraries() I will not have all defines configuration correctly.
I am using cross-compiler and in my toolchain.cmake I use to define compiler flags with command SET_TARGET_PROPERTIES(${TARGET} PROPERTIES COMPILE_DEFINITIONS GUCCI}"), but this is to late because this only is seen by my root CMakeLists.txt and not by my sub director CMakeLists.txt.
Your CMSIS_lib/CMakeLists.txt should look like this:
set(util_source_files
calculate.c
calculate.h
)
add_library(util ${util_source_files})
target_include_directories(util ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(util PUBLIC GUCCI)
Note target_compile_definitions line, with PUBLIC parameter: it instructs cmake to use -DGUCCI compiler option while compiling util, and all targets that are linked against util.
Also note change in target_include_directories. You placed header file as parameter, but you should place directory instead.

how to use CMake file (GLOB SRCS *. ) with a build directory

this is my current CMakeLists.txt file
cmake_minimum_required(VERSION 3.3)
set(CMAKE_C_FLAGS " -Wall -g ")
project( bmi )
file( GLOB SRCS *.cpp *.h )
add_executable( bmi ${SRCS})
This builds from my source directory, but I have to clean up all the extra files after. My question is how do I build this from a build directory if all my source files are in the same source directory?
thanks
If you really need to use file(GLOB …), this CMakeLists.txt should work :
cmake_minimum_required(VERSION 3.3)
project(bmi)
add_definitions("-Wall" "-g")
include_directories(${PROJECT_SOURCE_DIR})
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/*.cpp)
add_executable(bmi ${SRC_FILES})
In this case you have to launch cmake from your build directory every time you add or delete a source file :
cmake <your_source_dir> -G <your_build_generator>
As Phil reminds, CMake documentation doesn't recommend this use of GLOB. But there are some exceptions. You'll get more information on this post.
If you don't meet those exceptions, you'd rather list your source files than use GLOB :
set(SRC_FILES ${PROJECT_SOURCE_DIR}/main.cpp
${PROJECT_SOURCE_DIR}/bmi.cpp
… )
NB : if you have #include of your .h files in .cpp files, I don't see any reason to put them in add_executable, you just need to specify include directory with include_directories.
Cmake used to only update the list of source files if CMakeLists.txt was changed since the last cmake run or if cmake was used to configure the project again.
In cmake 3.11.0 recursive search and automatic re-configuration on adding or deleting source files was added. Since then you can use the following snippet:
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.11.0")
file(GLOB_RECURSE SOURCE_FILES CONFIGURE_DEPENDS *.cpp *.h)
else()
file(GLOB SOURCE_FILES *.cpp *.h */*.h */*.cpp)
endif()
The file() command after the else() provides at least a bit of backwards compatibility: It still searches for source files in the current folder and its direct subfolders. But it doesn't automatically recognize if there are new files or old files have been deleted.
Note that VERSION_GREATER_EQUAL is only available in cmake >= 3.7

CMake copy files using copy_if_different recursively

I am new to CMake. I have got my project compiling. I have a structure like this
PROJECT
SRC
test
.cpp (all source files) and CMakeLists.txt for this folder (creating a static library)
example
.cpp (all source files) and CMakeLists.txt for this folder (creating a static library)
Include
test
.h (all the header files)
example
.h (all the header files)
build
CMakeLists.txt (Main CMakelist file)
lib
test (contains the generated files)
example (contains the generated files)
The question is how do I copy all static files ie. from test and example folder and place them in a different folder outside the binary structure recursively?
My main CMakeLists.txt file:
PROJECT(copythefiles)
SET(CMAKE_CURRENT_BINARY_DIR ".")
add_subdirectory(/src/test /lib/test) # I am specifying the location where the files are to be generated
add_subdirectory(/src/example /lib/example)
ADD_CUSTOM_TARGET( a ALL COMMAND ${CMAKE_COMMAND} -E echo "\{X}" > ${CMAKE_CURRENT_BINARY_DIR}/lib/test/libtest.a )
ADD_CUSTOM_COMMAND(TARGET a POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_BINARY_DIR}/lib/test/libtest.a
${CMAKE_CURRENT_BINARY_DIR}/lib/libtest.a )
This copies the files. But I have around 20 projects and I would like to do it recursively.
Thanks :)
It's normal to have your top-level CMakeLists.txt file in the root of your project. If you move your CMakeLists.txt out of "/build" to the root, then you should be able to call add_subdirectory without having to specify the binary path for each case.
Assuming you move the CMakeLists file, then you can insert this before the add_subdirectory calls:
function(MoveLib TheTarget)
add_custom_command(TARGET ${TheTarget} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E
copy $<TARGET_FILE:${TheTarget}> ${CMAKE_SOURCE_DIR}/lib)
endfunction()
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_SOURCE_DIR}/lib)
This then allows you to add e.g. MoveLib(my_test) inside the libraries' CMakeLists.txt files, where my_test is the name of the library concerned.
A copy of all libraries will then end up in "/lib". If you're not really wanting copies, then you should have a look at the ARCHIVE_OUTPUT_DIRECTORY property. If you simply add
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)
then all your static libraries will end up in "/lib". There are a couple of things to watch for here though.
Shared libraries aren't covered by ARCHIVE_OUTPUT_DIRECTORY. The details for shared libs are in the docs for ARCHIVE_OUTPUT_DIRECTORY though.
Also, some generators (e.g. MSVC) append a per-configuration subdirectory to the specified directory, so you'd end up with "/lib/Debug", "/lib/Release", etc. You can circumvent this by setting the configuration-specific versions of CMAKE_ARCHIVE_OUTPUT_DIRECTORY to all point to ${CMAKE_SOURCE_DIR}/lib:
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_SOURCE_DIR}/lib)