59

I have two questions relative to CMake

  1. Assume that we have a variable ${MY_CURRENT_DIR} that contains the path of a directory that contains several subdirectories : mydir1, mydir2 and mydir3. I want to detect these subdirectories and put their names into ${SUBDIRS} (not the complete path of these directories, only their name). How to do that automatically ?

  2. Assume that ${SUBDIRS} contains "mydir1 mydir2 mydir3". How to replace

    ADD_SUBDIRECTORY(mydir1)
    ADD_SUBDIRECTORY(mydir2)
    ADD_SUBDIRECTORY(mydir3)
    

by a loop over ${SUBDIRS}?

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
Vincent
  • 57,703
  • 61
  • 205
  • 388

4 Answers4

123
  1. Use this macro:

    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()
    

    Example:

    SUBDIRLIST(SUBDIRS ${MY_CURRENT_DIR})
    
  2. Use foreach:

    FOREACH(subdir ${SUBDIRS})
      ADD_SUBDIRECTORY(${subdir})
    ENDFOREACH()
    
Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
refaim
  • 1,875
  • 1
  • 14
  • 9
  • 16
    for performance improvement, especially if `dirlist` is long, use `LIST(APPEND dirlist ${child})` instead of `SET(dirlist ${dirlist} ${child})` [see here](http://www.cmake.org/Wiki/CMake_Performance_Tips#Use_LIST.28APPEND_....29) – ParokshaX Mar 05 '14 at 11:07
  • 2
    Important note: this won't work with `GLOB_RECURSE`, only with `GLOB` – Synxis Mar 29 '16 at 16:03
  • 4
    GLOB_RECURSE has default LIST_DIRECTORIES false while GLOB has default LIST_DIRECTORIES true. If you use recurse you will have to set it to true – Jaco Oct 12 '16 at 09:57
  • 6
    One may want to modify the if-condition to check, if the folder includes a `CMakeLists.txt` file to only add suitable directories and not something like .git, which leads to errors during cmake: `IF(IS_DIRECTORY ${curdir}/${child} AND EXISTS ${curdir}/${child}/CMakeLists.txt)` – Stanley F. Apr 18 '19 at 09:16
3

In regard to answer above: Use this macro:

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()

Example:

SUBDIRLIST(SUBDIRS ${MY_CURRENT_DIR})

I had trouble with this FILE(GLOB command. (I'm on cmake 3.17.3) Otherwise the macro works great. I was getting FILE GLOB errors, something like "FILE GLOB requires a glob expression after the directory." (Maybe it didn't like RELATIVE and/or just using the curdir as the fourth paramter.)

I had to use:

FILE(GLOB children ${curdir}/*)

(taking out RELATIVE and the first ${curdir} (Please note my cmake version above, that could've been my issue (I'm unfamiliar with glob so far.).)

Dharman
  • 30,962
  • 25
  • 85
  • 135
skittlebiz
  • 359
  • 4
  • 8
  • Commenting on my own answer as I just realized FILE(GLOB was not expanding my ${curdir} variable, so that was probably the cause of my error. So you might not need to make these updates. – skittlebiz Feb 09 '21 at 17:13
  • You can use RELATIVE ${CMAKE_SOURCE_DIR}/${curdir} After relative Cmake expects an absolute path. – Fox1942 Feb 08 '22 at 11:59
3

For a modern simpler solution try this
For cmake 3.7 and above the GLOB module got a LIST_DIRECTORIES option

This link explains glob patterns

file(GLOB sources_list LIST_DIRECTORIES true YourGLOBPattern)
foreach(dir ${sources_list})
    IF(IS_DIRECTORY ${dir})
        add_subdirectory(${dir})
    ELSE()
        CONTINUE()
    ENDIF()
endforeach()
JN98ZK
  • 43
  • 1
  • 8
  • 1
    I don't see how this is a simpler version, to me it's just the same answer as the accepted answer from 2011 but with extra unneeded code. The LIST_DIRECTORIES option was added in cmake 3.3 (not 3.7) and adds the possibility to omit directories from the output when set to false. It defaults to true (outputting directories), so, not having this option equals the same behavior as you setting it to true. – ckielstra Feb 09 '22 at 21:47
0

@refaim answer didn't work for me with CMake 3.21.1, I had to do small changes:

MACRO(SUBDIRLIST result curdir)
    FILE(GLOB children ${curdir}/*) # This was changed
    SET(dirlist "")
    FOREACH(child ${children})
        IF(IS_DIRECTORY ${child}) # This was changed
            LIST(APPEND dirlist ${child})
        ENDIF()
    ENDFOREACH()
    SET(${result} ${dirlist})
ENDMACRO()