I have an little library which has an file structure like this:
CMakeLists.txt
LibFoo/
Foo.h
FooWin.cpp
FooWin.inl
FooPosix.cpp
FooPosix.inl
And when i have to build the library in a specific OS (for example Windows) they should contain in the list before using file(GLOB_RECURSE) using a macro:
macro( add_recursive dir retVal)
file( GLOB_RECURSE ${retVal} ${dir}/*.h ${dir}/*.cpp ${dir}/*.c ${dir}/*.inl )
endmacro()
and my excluding pattern is like this (when Windows is the build host): *Posix.* but doesn't work, i tried with this approach:
macro( add_recursive dir retVal pattern)
file( GLOB_RECURSE ${retVal} ${dir}/*.h ${dir}/*.cpp ${dir}/*.c ${dir}/*.inl EXCLUDE PATTERN "${pattern}")
endmacro()
but the POSIX files still here anyways, CMake doesn't report an error or anything suggested about that.
You can use list filtering to remove items after the GLOB_RECURSE:
list(FILTER ${retVal} EXCLUDE REGEX "Posix.")
There is no EXCLUDE option for file(GLOB_RECURSE ...) command flow. You probably take this option from file(COPY|INSTALL ...), which is a different command flow.
You may iterate over list, obtained from file(GLOB_RECURSE) and exclude needed files manually:
macro( add_recursive dir retVal)
# Store unfiltered list of files into temporary list
file( GLOB_RECURSE _tmp_list ${dir}/*.h ${dir}/*.cpp ${dir}/*.c ${dir}/*.inl )
# Resulted list is initially empty. Copy all needed elements into it.
set(${retval})
foreach(f ${_tmp_list})
if(NOT f MATCHES "Posix.")
list(APPEND ${retVal} ${f})
endif()
endforeach()
endmacro()
This piece of code almost worked for me.
cmake nags about the set not having enough arguments and discontinues the macro.
This snippet also makes the extention and exclude filter variable
macro( add_recursive retVal dir ext excl)
# Store unfiltered list of files into temporary list
file( GLOB_RECURSE _tmp_list ${dir}/${ext})
# Resulted list is initially empty. Copy all needed elements into it.
set(${retval} "")
foreach(f ${_tmp_list})
if(NOT f MATCHES ${excl})
list(APPEND ${retVal} ${f})
endif()
endforeach()
endmacro( add_recursive )
#example of usage
add_recursive(inc "./" "*.h" "/exclude_folder/")
Related
In CMake, how can I get a non-recursive list of directories that exist in a certain directory?
I can see that using GLOB isn't recommended.
Why wouldn't GLOB be recommended? That's what the GLOB is for. I love GLOB, especially in cases like this where it's extremely helpful to grab multiple items.
The macro from this question may be what you're looking for.
MACRO(SUBDIRLIST result curdir)
FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
SET(dirlist "")
FOREACH(child ${children})
IF(IS_DIRECTORY ${curdir}/${child})
LIST(APPEND dirlist ${child})
ENDIF()
ENDFOREACH()
SET(${result} ${dirlist})
ENDMACRO()
You basically grab the items from the current source directory, check to see if it's a directory, and if so, append it to a list.
Credit to refaim in the link for that macro.
I want to do a Macro that gets a list of the sub-sub-directories that contain a specific type of files, in my case .jar files.
This macro is getting me all the sub-sub-directories:
MACRO(SUBSUBDIRLIST result curdir)
FILE(GLOB children RELATIVE ${curdir} ${curdir}/*/*)
SET(dirlist "")
FOREACH(child ${children})
IF(IS_DIRECTORY ${curdir}/${child})
LIST(APPEND dirlist ${child})
ENDIF()
ENDFOREACH()
SET(${result} ${dirlist})
ENDMACRO()
SUBSUBDIRLIST(TUTORIALS ${CMAKE_CURRENT_SOURCE_DIR})
What I now need is to find a way to check if a directory contains any .jar file.
Can I change the IF to do something like IF(child ${children} AND ${child} CONTAINS *.jar)?
While if command supports many ready-made checks, not all checks can be expressed directly via if. But you are free to use other commands, and check their result via if.
For check whether given directory contains specific type of files, FILE(GLOB) can be effectively used:
FILE(GLOB jars "${child}/*.jar")
if(jars)
# There are .jar files in directory referred by 'child'
endif()
I'm trying to call add_library for all files with certain endings.
The dir structure is:
src
| - CMakeLists.txt (1)
| - main.cpp
| - gui
| - CMakeLists.txt (2)
| - some source and header files
So currently all cc files are in the gui directory.
(1) CMakeLists.txt:
file( GLOB_RECURSE my_sources *.cc )
message(STATUS "my_sources = ${my_sources}")
add_subdirectory( gui )
add_library( my_src ${my_SOURCES} )
target_link_libraries( my_src
my_gui
)
qt5_use_modules( my_src Core Gui Widgets)
(2) CMakeLists.txt:
file( GLOB my_gui_sources *.cc)
add_library( my_gui ${my_gui_sources} )
qt5_use_modules( my_gui Core Gui Widgets)
But I keep getting this output:
You have called ADD_LIBRARY for library my_src without any source files. This typically indicates a problem with your CMakeLists.txt file
-- my_sources = /home/bla/bla/src/gui/BorderLayout.cc;...;/home/bla/bla/my/src/gui/MainWindow.cc
-- my_gui_sources = /home/bla/bla/my/src/gui/BorderLayout.cc;...;/home/bla/bla/my/src/gui/MainWindow.cc
-- Configuring done
-- Generating done
-- Build files have been written to: /home/bla/bla/my/build
I know that I currently don't need the add_library in the first CMakeLists.txt, but later I will. I changed the first GLOB to GLOB_RECURSE, so that it finds at least anything.
For some reason your
file( GLOB my_gui_sources *.cc *.h)
Is not finding any file. To debug, you can print:
message(STATUS "my_gui_sources = ${my_gui_sources}")
Probably you want to use GLOB_RECURSE, which search in sub-directories:
file( GLOB_RECURSE my_gui_sources *.cc *.h)
Note that you don't need to add headers files to the source list.
Take care that you will have to rerun cmake every time you add a file to your project (cmake won't be called automatically, thing that instead happens if you touch one of the cmake files).
Link to documentation of command "file"
Edit:
The actual problem is that in your first CMakeLists.txt file you are using inconsistent naming for your variable (note that casing is important), therefore you have to change your add_library command to:
add_library( my_src ${my_sources} )
Note (off the records :-) ): the fact that casing is important for variable names might be confusing because, on the other hand, in cmake command names are case insensitive. It's also sometimes weird to notice that the character - (minus) might be used as part of the variable name: using _ (underscore) is most of the time preferable.
I would like to get all headers added to cmake project. The use case is that I'd get this list of headers and call some custom validation on them. I would really love this to be a query mechanism to mitigate errors in oversight.
I am not interested in globbing the file system as headers may exist that are not appropriate for every platform. It's also bad.
This is what I would like the usage to look like.
add_library(example_lib
foo.h
foo.cpp
bar.h
bar.cpp
)
add_executable(example main_example.cpp)
target_link_libraries(example example_lib)
# this is the feature I am interested in
get_target_headers(example_header example)
# alternatively
get_target_headers(example_header example example_lib)
do_custom_thing("${example_header}")
A more manual way of doing this would be something like the below. I'd just reuse the example_header variable to do the custom validation.
set(example_header
foo.h
bar.h
)
set(example_source
foo.cpp
bar.cpp
)
add_library(example_lib
${example_header}
${example_source}
)
add_executable(example main_example.cpp)
target_link_libraries(example example_lib)
do_custom_thing("${example_header}")
This is what I'm doing now and it works, I am just wondering if there is a better way.
If all your headers have a ".h" suffix, you could use something like:
function(get_target_headers Headers MainTarget)
# Gather list of MainTarget's dependencies
get_target_property(Dependencies ${MainTarget} LINK_LIBRARIES)
set(AllTargets ${MainTarget})
foreach(Dependency ${Dependencies})
# If this is a CMake target, add it to the list
if(TARGET ${Dependency})
list(APPEND AllTargets ${Dependency})
endif()
endforeach()
# Gather each target's list of source files ending in ".h"
foreach(Target ${AllTargets})
get_target_property(Sources ${Target} SOURCES)
foreach(Source ${Sources})
string(REGEX MATCH "^.*\\.h$" Header ${Source})
if(Header)
list(APPEND AllHeaders ${Header})
endif()
endforeach()
endforeach()
# Since functions have their own scope, set the list in the parent scope
set(${Headers} ${AllHeaders} PARENT_SCOPE)
endfunction()
and invoke it using your first choice:
get_target_headers(example_header example)
I'm wondering if there is a way to print out all accessible variables in CMake. I'm not interested in the CMake variables - as in the --help-variables option. I'm talking about my variables that I defined, or the variables defined by included scripts.
I'm currently including:
INCLUDE (${CMAKE_ROOT}/Modules/CMakeBackwardCompatibilityCXX.cmake)
And I was hoping that I could just print out all the variables that are here, instead of having to go through all the files and read what was available - I may find some variables I didn't know about that may be useful. It would be good to aid learning & discovery. It is strictly for debugging/development.
This is similar to the question in Print all local variables accessible to the current scope in Lua, but for CMake!
Has anyone done this?
Using the get_cmake_property function, the following loop will print out all CMake variables defined and their values:
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
This can also be embedded in a convenience function which can optionally use a regular expression to print only a subset of variables with matching names
function(dump_cmake_variables)
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
if (ARGV0)
unset(MATCHED)
string(REGEX MATCH ${ARGV0} MATCHED ${_variableName})
if (NOT MATCHED)
continue()
endif()
endif()
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
endfunction()
To print environment variables, use CMake's command mode:
execute_process(COMMAND "${CMAKE_COMMAND}" "-E" "environment")
Another way is to simply use:
cmake -LAH
From the manpage:
-L[A][H]
List non-advanced cached variables.
List cache variables will run CMake and list all the variables from the CMake cache that are not marked as INTERNAL or ADVANCED. This will effectively display current CMake settings [...].
If A is specified, then it will display also advanced variables.
If H is specified, it will also display help for each variable.
ccmake is a good interactive option to interactively inspect cached variables (option( or set( CACHE:
sudo apt-get install cmake-curses-gui
mkdir build
cd build
cmake ..
ccmake ..
Another way to view all cmake's internal variables, is by executing cmake with the --trace-expand option.
This will give you a trace of all .cmake files executed and variables set on each line.
based on #sakra
function(dump_cmake_variables)
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
if (ARGV0)
unset(MATCHED)
#case sensitive match
# string(REGEX MATCH ${ARGV0} MATCHED ${_variableName})
#
#case insenstitive match
string( TOLOWER "${ARGV0}" ARGV0_lower )
string( TOLOWER "${_variableName}" _variableName_lower )
string(REGEX MATCH ${ARGV0_lower} MATCHED ${_variableName_lower})
if (NOT MATCHED)
continue()
endif()
endif()
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
endfunction()
dump_cmake_variables("^Boost")
variable names are case sensitive
btw if you are interested in boost, it is Boost_INCLUDE_DIRS not BOOST_INCLUDE_DIRS, and it is Boost_LIBRARIES not BOOST_LIBRARIES, and by mistake I had BOOST_LIBRARIES instead of Boost_LIBRARIES, https://cmake.org/cmake/help/v3.0/module/FindBoost.html , better example for boost:
set(Boost_USE_STATIC_LIBS ON)
find_package(Boost REQUIRED COMPONENTS RANDOM)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(myfile PRIVATE
${Boost_LIBRARIES}
)
You can use message :
message([STATUS] "SUB_SOURCES : ${SUB_SOURCES}")
None of the current answers allowed me to see the variables in my project subdirectory. Here's a solution:
function(print_directory_variables dir)
# Dump variables:
get_property(_variableNames DIRECTORY ${dir} PROPERTY VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
get_directory_property(_variableValue DIRECTORY ${dir} DEFINITION ${_variableName})
message(STATUS "DIR ${dir}: ${_variableName}=${_variableValue}")
endforeach()
endfunction(print_directory_variables)
# for example
print_directory_variables(.)
print_directory_variables(ui/qt)