CMake file GLOB_RECURSE with complex globs - cmake

I would like to use file(GLOB_RECURSE... as follows:
file(GLOB_RECURSE _tmp_files
LIST_DIRECTORIES false
"${mydir}/*.cpp|${mydir}/*.h")
This -- along with variations such as ${mydir}/*{.cpp,.h} -- yields an empty list.
However, this works as expected -- produces all the *.cpp files in the ${mydir} along with its subdirectories:
file(GLOB_RECURSE _tmp_files
LIST_DIRECTORIES false
"${mydir}/*.cpp")
So, does the file( feature in CMake indeed support "full" globbing? In particular, how to glob for a pattern {*.cpp,*.h} using file(?

So, does the file( feature in CMake indeed support "full" globbing?
Yes.
Your expressions do not work the way you expect them to work. {..,..} is not a way to match multiple suffixes in glob, { , } are matched literally and have no special meaning in glob. "${mydir}/*.cpp|${mydir}/*.h" is not an OR in glob - | is matched literally, it has no special meaning in glob. See man 7 glob.
how to glob for a pattern {.cpp,.h} using file(?
Write it twice.
file(GLOB_RECURSE _tmp_files
LIST_DIRECTORIES false
${mydir}/*.cpp
${mydir}/*.h
)

Related

how to use regular expression to include file with postfix (.h, .hpp and .hxx) in cmake?

I want to install all the header file under a directory.
i know regular expression can do that. but i do google a lot, not found how to include these three kind of file together.
I used command like this:
file(GLOB INFRA ${LOCAL_SRC_PATH}/infra/*.h[|p|x][|p|x])
to make INFRA variable to save all header file, but i found it ignore .h
I know this is quite easy, can you kindly help on this?
A globbing expression is not a regex; you need to use 3 separate commands to match .h, .hpp and .hxx files. Even as a regex this would fail, since the character groups match characters |, p and x, but they don't match the empty string.
In this case you'd probably use
set(PATH_PREFIX "${LOCAL_SRC_PATH}/infra/*")
file(GLOB INFRA_H "${PATH_PREFIX}.h")
file(GLOB INFRA_HPP "${PATH_PREFIX}.hpp")
file(GLOB INFRA_HXX "${PATH_PREFIX}.hxx")
set(INFRA ${INFRA_H} ${INFRA_HPP} ${INFRA_HXX})
In this case I recommend using install(DIRECTORY) though; this also allows you to use a regex instead of a globbing expression:
install(DIRECTORY "${LOCAL_SRC_PATH}/infra"
TYPE INCLUDE
FILES_MATCHING REGEX ".*\\.(h|hpp|hxx)")

Joining paths in CMake

I am trying to join two paths together:
SET(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}/inkscape")
but a string concatenation does not really do it when CMAKE_INSTALL_LIBDIR contains an absolute path.
Is there a CMake function that takes multiple path arguments and joins relative paths right of the rightmost absolute path to the absolute path, like Python’s os.path.join does?
Examples from Python interpreter showing desired behaviour:
>>> from os.path import join
>>> join("/foo/bar", "/baz/qux")
'/baz/qux'
>>> join("foo/bar", "/baz/qux")
'/baz/qux'
>>> join("/foo/bar", "./baz/qux")
'/foo/bar/./baz/qux'
>>> join("/foo/bar", "../baz/qux")
'/foo/bar/../baz/qux'
>>> join("./foo/bar", "baz/qux")
'./foo/bar/baz/qux'
I need to handle both the cases where the prefix is absolute (e.g. CMAKE_INSTALL_PREFIX), and where it is relative (e.g. $ORIGIN/.. or ${prefix} often needed for pkg-config files). And orthogonally, I need to handle both Linux distributions that use relative CMAKE_INSTALL_LIBDIR, and those that use an absolute one.
EDIT: UPDATED BASED ON OP FEEDBACK
Nothing in CMake supports exactly what you want out of the box. However, you can easily create your own if statements (using IS_ABSOLUTE) for the four scenarios you describe:
if(IS_ABSOLUTE ${PREFIX_DIR})
if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
# Both absolute.
set(CMAKE_INSTALL_PKGLIBDIR ....)
else()
# Prefix is absolute, but LIBDIR is relative.
set(CMAKE_INSTALL_PKGLIBDIR ....)
endif()
else()
if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
# Prefix is relative, but LIBDIR is absolute.
set(CMAKE_INSTALL_PKGLIBDIR ....)
else()
# Both are relative.
set(CMAKE_INSTALL_PKGLIBDIR ....)
endif()
endif()
This can be generalized and made into a function, called directory_join() or something, and could be used throughout your CMake files wherever you need it.
Assuming I'm interpreting your question correctly, you can accomplish a join mechanism with CMake. If you have some absolute path and some relative path(s) you want to join, the get_filename_component() command can help. Here's a general example:
set(ABS_PATH "C:/the/absolute/path")
set(REL_PATH "../../some/other/relative/path")
# Concatenate your absolute and relative path(s) here.
get_filename_component(COMBINED_PATH ${ABS_PATH}/${REL_PATH} ABSOLUTE)
# Print our merged path to verify.
message(STATUS "COMBINED_PATH: ${COMBINED_PATH}")
We can see by the print-out that the new variable COMBINED_PATH joins the two paths, resolving any relative-ness.
COMBINED_PATH: C:/the/some/other/relative/path
September 2020: cmake_path command has just been merged: https://gitlab.kitware.com/cmake/cmake/-/merge_requests/5158
I have sent an example implementation to the upstream issue. It supports multiple arguments like the Python’s os.path.join and works on Linux at least:
# Modelled after Python’s os.path.join
# https://docs.python.org/3.7/library/os.path.html#os.path.join
# Windows not supported
function(join_paths joined_path first_path_segment)
set(temp_path "${first_path_segment}")
foreach(current_segment IN LISTS ARGN)
if(NOT ("${current_segment}" STREQUAL ""))
if(IS_ABSOLUTE "${current_segment}")
set(temp_path "${current_segment}")
else()
set(temp_path "${temp_path}/${current_segment}")
endif()
endif()
endforeach()
set(${joined_path} "${temp_path}" PARENT_SCOPE)
endfunction()
It would still be nice if CMake supported such essential functionality out of the box.

CMake exclude files from a given pattern after file(GLOB_RECURSE)

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/")

CMake Warning: You have called ADD_LIBRARY for library my_src without any source files

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.

CMAKE aux_source_directory exclude pattern

I want to include all filed in source directory leaving one file.
Is there any way to using aux_source_directory or anything else I can include all files leaving that file ?
There are two possible solutions:
Use file (GLOB ... instead of aux_source_directory with a globbing expression that does not match that one file but includes all the others, e.g.:
file(GLOB _srcFiles "src/f[1-3].cpp")
This will match match files f1.cpp, f2.cpp, f3.cpp, but not f4.cpp.
Or use aux_source_directory and then remove the file to be excluded explicitly with a list(REMOVE_ITEM command, e.g.:
aux_source_directory(src _srcFiles)
list(REMOVE_ITEM _srcFiles "src/f4.cpp")