How to find all sub-directories named include in cmake - cmake

How do I find all directories named include in cmake? For instance, if the directory structure is like that:
root
include/
a/
include/
b/
c/
d/
include/
I'd like to assign to a variable all found paths:
root/include
root/a/include
root/b/c/d/include
I tried to use GLOBE_RECURSE, but with no success.

file(GLOB_RECURSE include_dirs LIST_DIRECTORIES true
RELATIVE ${CMAKE_SOURCE_DIR} "include")
This works since CMake 3.3, as option LIST_DIRECTORIES appears only in this version. (By default, for GLOB_RECURSE mode option LIST_DIRECTORIES is false).

Related

Using CMake GLOB_RECURSE to find directories

I have the following directory structure:
.
├── CMakeLists.txt
└── deps
├── eigen
│   └── include
│   ├── Eigen
│   └── eigen.h
└── osg
├── include
│   └── Osg
└── lib
└── libosg.so
I am trying to get a maximum of the files I don't need to deploy my software, i.e. the libraries.
I tried to create a globbing expression that matches the *.h files and the include directories:
file(GLOB_RECURSE
FOUND_FILES
LIST_DIRECTORIES true
${CMAKE_SOURCE_DIR}/deps/*/include
${CMAKE_SOURCE_DIR}/deps/*/*.h)
message(STATUS "Files are ${FOUND_FILES}")
However, for some reason, the variable FOUND_FILES contains deps/osg/lib. What did I not understand about the GLOB_RECURSE function?
- deps/eigen/include
- deps/eigen/include/eigen.h
- deps/osg/include
- deps/osg/lib
- deps/eigen/include
- deps/osg/include
- deps/osg/lib
I think this could be a workaround:
file(GLOB_RECURSE
FOUND_FILES
LIST_DIRECTORIES true
${CMAKE_SOURCE_DIR}/deps/*/include
)
list(FILTER FOUND_FILES INCLUDE REGEX "^${CMAKE_SOURCE_DIR}/deps/.*/include$")
file(GLOB_RECURSE
tmp
${CMAKE_BINARY_DIR}/deps/*/*.h
)
list(APPEND FOUND_FILES ${tmp})
I guess this is a bug when GLOB_RECURSE is used with LIST_DIRECTORIES true with an expression that has * not on the last entry in the path. Once a directory containing the matched entry is matched in cmake:Glob.cxx#L404, next directories will be added recursively to the output unconditionally at this add_file() in cmake Glob.cxx#L316. So once a ${CMAKE_SOURCE_DIR}/deps/* directory has a include directory or file inside it, all directories recursively from ${CMAKE_SOURCE_DIR}/deps/ are added to the output. Files are not added, as they are checked against the regex at cmake Glob.cxx:#L326.
It appears that CMake drops the last component of the globbing expression when using GLOB_RECURSE to filter directories. This is why CMake does not filter further for the include directory in your example. This may be a bug in the CMake GLOB_RECURSE implementation for directories, or an oversight in the CMake documentation.
EDIT: Solution 1 (does not work):
You can instead simulate recursion using the globbing pattern itself. Use CMake's GLOB instead, and use ** in the globbing pattern to match on anything with one or more characters between deps/ and /include:
file(GLOB
FOUND_FILES
LIST_DIRECTORIES true
${CMAKE_SOURCE_DIR}/deps/**/include
${CMAKE_BINARY_DIR}/deps/**/*.h)
)
message(STATUS "Files are ${FOUND_FILES}")
Solution 2 (ugly):
You could manually add the levels of directories for CMake to search for include folders:
file(GLOB
FOUND_FILES
LIST_DIRECTORIES true
${CMAKE_SOURCE_DIR}/deps/*/include
${CMAKE_SOURCE_DIR}/deps/*/*/include
...
)
However, this is not a very scalable solution.

How can I set CMAKE_MODULE_PATH for doing regular and out-of-source builds in CMake?

I have a line like this in my CMakeLists.txt
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "cmake")
This is so that some custom libraries can be found with a directory tree that looks like this:
CMakeLists.txt
cmake/
|-- FindSomeLibrary.cmake
|-- FindAnotherLibrary.cmake
Normally I build simply like this:
cmake .
Which works fine. However, I want to provide a debug and release build using a script like this:
mkdir release
cd release
cmake -DCMAKE_BUILD_TYPE=Release ../
However, now it cannot find the cmake modules.
Is there a way to set CMAKE_MODULE_PATH such that it works for both in-source and out-of-source builds?
As proposed in the comments:
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
and always try to set all paths relative to some CMAKE_* directory. That way you will miss many errors. ; )
As CMAKE_MODULE_PATH is a list, so it's better to use:
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

How to automatically move *.pb.h and *.pb.h under /include and /src directories?

I am developing a library which uses Google Protocol Buffers (protobuf) and CMake. The project has the following directory tree.
MyProject/
MyProject/include/myproject/
MyProject/include/myproject/some_classes.h
MyProject/src/
MyProject/src/some_classes.cc
MyProject/src/foo.proto
MyProject/CMakeList.txt
CMakeList.txt has the following lines to generate protobuf source and header files.
include_directories(${libCHEC_SOURCE_DIR}/include)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SOURCES)
find_package(Protobuf REQUIRED)
file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/src/*.proto")
protobuf_generate_cpp(ProtoSources ProtoHeaders ${ProtoFiles})
list(APPEND EXTLIBS ${PROTOBUF_LIBRARIES})
add_library(MyLibrary SHARED ${SOURCES} ${ProtoSources})
target_link_libraries(MyLibrary ${EXTLIBS})
When I execute cmake, foo.pb.h and foo.pb.cc are generated under the build directory (i.e. the directory where I executed cmake). It looks that this is the default behavior. But I would like to put foo.pb.h and foo.pb.cc under include/myproject and src directories, respectively.
How can I change the locations of the files generated by protoc?
I would strictly advise against placing generated files in the source tree.
CMake puts a lot of effort into separating the build and source trees. Forcing it to give up that separation has several disadvantages. Among the most prominent is the fact that version control will then have to deal with unversioned generated files in the source tree, and furthermore it may no longer be possible to have multiple builds targetting different architectures sharing the same source tree.
A better approach is to keep the files in the binary tree and adjust your target_include_directories accordingly. There is no shame in using generated files from the binary tree as sources, so don't hesitate to do it.
I added MyProject/misc/myprotobuf.cmake which had a modified version of PROTOBUF_GENERAGE_CPP.
function(MY_PROTOBUF_GENERATE_CPP PATH SRCS HDRS)
if(NOT ARGN)
message(SEND_ERROR "Error: PROTOBUF_GENERATE_CPP() called without any proto files")
return()
endif()
if(PROTOBUF_GENERATE_CPP_APPEND_PATH)
# Create an include path for each file specified
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(ABS_PATH ${ABS_FIL} PATH)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
endif()
endforeach()
else()
set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
endif()
if(DEFINED PROTOBUF_IMPORT_DIRS)
foreach(DIR ${PROTOBUF_IMPORT_DIRS})
get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
endif()
endforeach()
endif()
set(${SRCS})
set(${HDRS})
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(FIL_WE ${FIL} NAME_WE)
list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${PATH}/${FIL_WE}.pb.cc")
list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${PATH}/${FIL_WE}.pb.h")
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${PATH})
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${PATH}/${FIL_WE}.pb.cc"
"${CMAKE_CURRENT_BINARY_DIR}/${PATH}/${FIL_WE}.pb.h"
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE}
ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR}/${PATH} ${_protobuf_include_path} ${ABS_FIL}
DEPENDS ${ABS_FIL}
COMMENT "Running C++ protocol buffer compiler on ${FIL}"
VERBATIM )
endforeach()
set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE)
set(${SRCS} ${${SRCS}} PARENT_SCOPE)
set(${HDRS} ${${HDRS}} PARENT_SCOPE)
endfunction()
MyProject/CMakeLists.txt has the following two lines now.
include(${CMAKE_CURRENT_SOURCE_DIR}/misc/myprotobuf.cmake)
file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/misc/*.proto")
my_protobuf_generate_cpp(generated_files/myproject ProtoSources ProtoHeaders ${ProtoFiles})
include_directories(${CMAKE_CURRENT_BINARY_DIR}/generated_files)
You can manipulate the directory structure for your generated protobuf sources by modifying the source directory and cmakelists in a particular way.
An example:
MyProject/
MyProject/src
MyProject/src/CMakeLists.txt (1)
MyProject/src/ProtoProject/CMakeLists.txt (2)
MyProject/src/ProtoProject/MyProject/foo.proto
CMakeLists.txt (1):
add_subdirectory(MyProject)
# generated headers
target_include_directories(ProtoProject PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
CMakeLists.txt (2):
set(proto_files "foo.proto")
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${proto_files})
add_library(ProtoProject STATIC ${PROTO_SRCS} ${PROTO_HDRS} ${proto_files})
Why this works
Each call to add_subdirectory() adds a directory level to the $CMAKE_CURRENT_BINARY_DIRECTORY variable. The generated files will end up at MyProject/src/ProtoProject/MyProject/foo.pb.h but your include directory will be MyProject/src/ProtoProject, so you can include files as MyProject/foo.pb.h

Adding google-test to a subfolder in a CMake project

I have just added google-test source code to libs/gtest-1.6.4 directory in my project. There is a libs/gtest-1.6.4/CMakeLists.txt file. In the top-most CMakeLists.txt, I have added add_subdirectory('libs/gtest-1.6.4'). The structure of the project is
|- CMakeLists.txt
|- src
|- CMakeLists.txt
|- *.h and *.cc
|- libs
|- gtest-1.6.4
|- CMakeLists.txt
|- gtest source code etc.
|- other subdirectories
Now I add #include "gtest/gtest.h" in one of the header file. Compilation fails with
gtest/gtest.h: No such file or directory
compilation terminated.
Here is the snippet of my src/CMakeLists.txt file.
set( Boost_USE_STATIC_LIBS ON )
find_package( Boost COMPONENTS graph regex system filesystem thread REQUIRED)
.. Normal cmake stuff ...
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS} )
# This line is added for google-test
INCLUDE_DIRECTORIES(${GTEST_INCLUDE_DIRS} ${COMMON_INCLUDES})
add_executable(Partitioner
print_function.cc
methods.cc
partitioner.cc
main.cc
)
TARGET_LINK_LIBRARIES(Partitioner ${Boost_LIBRARIES})
TARGET_LINK_LIBRARIES(Partitioner ${GTEST_LIBRARIES})
What am I missing?
Looking at GTest's CMakeLists.txt, it looks like their include path is ${gtest_SOURCE_DIR}/include. They also define the library as a CMake target called gtest (this is wrapped in a macro cxx_library(gtest ...) currently on line 70).
So it looks like you need to do:
...
# This line is added for google-test
INCLUDE_DIRECTORIES(${GTEST_INCLUDE_DIRS} ${COMMON_INCLUDES})
INCLUDE_DIRECTORIES(${gtest_SOURCE_DIR}/include ${COMMON_INCLUDES})
...
TARGET_LINK_LIBRARIES(Partitioner ${Boost_LIBRARIES})
TARGET_LINK_LIBRARIES(Partitioner ${GTEST_LIBRARIES})
TARGET_LINK_LIBRARIES(Partitioner ${Boost_LIBRARIES} gtest)
You'd also have to ensure that in your root CMakeLists.txt, you've called add_subdirectory(libs/gtest-1.6.4) before add_subdirectory(src) so that the GTest variables are correctly set when they're being used in "src/CMakeLists.txt".
As mentioned in accepted answer,
I did put my GoogleTest stuff up before my add_subdirectory() lines and it worked.
# The ROOT CMakeLists.txt
enable_testing()
include(CTest)
# https://google.github.io/googletest/quickstart-cmake.html
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/....zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
include(GoogleTest)
add_subdirectory(some)
add_subdirectory(other)
add_subdirectory(another)
And in one of those sub directory, I see tests work.
# A CMakeLists.txt in a sub directory
#enable_testing() # NOT REQUIRED, hence, commented out
#include(CTest) # NOT REQUIRED, hence, commented out
#include(GoogleTest) # NOT REQUIRED, hence, commented out
add_executable(
mytest
test/mytest.cpp
)
target_link_libraries(
mytest
gtest_main
)
gtest_discover_tests(
mytest
)

Compile sources in `src` directory to binaries in `bin` directory with CMake

My project directory contains a CMakeLists.txt file and src and include directories at its root. src also contains its own CMakeLists.txt, which is linked by the one at the root. Is there a way I can specify to CMake to set a default global build directory so that the syntax in src/CMakeLists.txt is close to the following?
include_directories(include)
add_executable(first main.cpp foo.cpp)
add_executable(second bar.cpp)
I would like this directory tree to be built:
CMakeLists.txt
src/
CMakeLists.txt
main.cpp
foo.cpp
bar.cpp
include/
...
bin/ (or build/)
first
second
You could set CMAKE_RUNTIME_OUTPUT_DIRECTORY:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
However, this won't create subdirectories inside bin/ for each different target.
If you want that, you could create a helper function to wrap add_subdirectories:
function(my_add_executable TargetName)
set(Files ${ARGV})
list(REMOVE_AT Files 0)
add_executable(${TargetName} ${Files})
set_target_properties(${TargetName} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY
"${CMAKE_SOURCE_DIR}/bin/${TargetName}")
endfunction()
then simply change your calls to:
my_add_executable(first main.cpp foo.cpp)
my_add_executable(second bar.cpp)
For further details, run
cmake --help-variable "CMAKE_RUNTIME_OUTPUT_DIRECTORY"
cmake --help-property "RUNTIME_OUTPUT_DIRECTORY"
cmake --help-property "RUNTIME_OUTPUT_DIRECTORY_<CONFIG>"