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.
Related
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.
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 using HINTS with find_library and was surprised to find that the last path had priority. Is this intentional or something that can be configured.
set(MY_HINT_PATHS_A "/path/to/a;/path/to/b")
find_library(MY_LIBRARY_A
NAMES MyLib
HINTS ${MY_HINT_PATHS_A}/lib
ONLY_CMAKE_FIND_ROOT_PATH NO_DEFAULT_PATH
)
set(MY_HINT_PATHS_B "/path/to/b;/path/to/a")
find_library(MY_LIBRARY_B
NAMES MyLib
HINTS ${MY_HINT_PATHS_B}/lib
ONLY_CMAKE_FIND_ROOT_PATH NO_DEFAULT_PATH
)
message("MY_LIBRARY_A: ${MY_LIBRARY_A}")
message("MY_LIBRARY_B: ${MY_LIBRARY_B}")
prints
MY_LIBRARY_A: /path/to/b/lib/libMyLib.a
MY_LIBRARY_B: /path/to/a/lib/libMyLib.a
I would have expected
MY_LIBRARY_A: /path/to/a/lib/libMyLib.a
MY_LIBRARY_B: /path/to/b/lib/libMyLib.a
CMake list is not a type, but an interpretation of string value. So any operations on list-variables are actually operations on strings.
${MY_HINT_PATHS_A}/lib doesn't append /lib to all elements in the list, it appends /lib only to the last element:
"/path/to/b;/path/to/a/lib"
It is absolutely equivalent to appending /lib to the variable's string-value.
For get desired effect you may use lib with PATH_SUFFIXES option to the find_library command. Or directly append /lib suffix to every element in the list.
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/")
I have a directory with files that either belong to a set that makes up a Qt project, and other files that do not. That is, files A.cxx, ADriver.cxx and A.ui all belong to a set that needs to be compiled with Qt options. I then have a file B.cxx that is non-qt. Then C.cxx, CDriver, and C.ui are another Qt set. There are tens of these, so I want to use globs rather than write each add_executable manually. I was thinking of doing something like
for(all ui files)
create an executable from the ui and its matching .cxx and *Driver.cxx"
end
Then all cxx files that "remain" (not used in the above loop) are non-Qt, and need to be compiled by themselves. My question is how to "subtract" files from a "set". That is, to use the method described above I'd have to have a set of all cxx files, and remove the ones that get used in the .ui file loop. Is this possible? Is there a better way to do something like this?
First, gather all files with a glob:
file(GLOB ALL_SRCS *)
Then select ui files and create Qt targets for them, substracting them from the ALL_SRCS list at the same time:
file(GLOB UIS *.ui)
foreach(ui ${UIS})
get_filename_component(f ${ui} NAME_WE)
# Do Qt stuff
qt4_wrap_ui( ${f}uis ${ui} )
qt4_wrap_cpp( ${f}srcs ${f}.cpp ${f}Driver.cpp )
add_executable( ${f} ${f}uis ${f}srcs )
list(REMOVE_ITEM ALL_SRCS ${ui} ${f}.cpp ${f}Driver.cpp)
endforeach()
After this you'll have all non-qt sources in ALL_SRCS.