7

I don't understand the source_group command in my CMakeLists.txt. When I do this it works:

file(GLOB INPUT_HEAD    KeyBoard.h Mouse.h)
source_group("Header Files\\Input" FILES ${INPUT_HEAD})

But this isn't working:

file(GLOB SHADERS ../Shaders/*.txt)
source_group("Source Files\\Shaders" FILES ${SHADERS})

Any suggestions on how to solve this? (I have read the documentation for this command, I don't understand whythis doesn't work)

Phil
  • 5,822
  • 2
  • 31
  • 60
Edvin
  • 1,841
  • 4
  • 16
  • 23

3 Answers3

18

You need to use the files in an actual target. For example they must be used in an add_library or add_executable statement, then they will be in a folder within that project. Also, I use ''/'' rather than \\ as a separator. You may also want to use set_property(GLOBAL PROPERTY USE_FOLDERS ON) to have the predefined cmake projects go into their own solution folder.

Phil
  • 5,822
  • 2
  • 31
  • 60
  • 1
    Ahh.. But shader files are neither a library or an executable. How should I proceed to include these files in the visual studio project? Should I just leave them be and force the user to open them in a separate text editor? – Edvin Jul 29 '15 at 11:57
  • 1
    As long as they do not have an extension that makes Visual Studio try to build them, I try to include ALL files with a target because I like having access to them from the IDE when I am using the source. I just put them into a separate folder. I have not had a problem with conflicting extensions, but if you do, I think there should be some [source property](http://www.cmake.org/cmake/help/v3.3/manual/cmake-properties.7.html#properties-on-source-files) you can set (either SYMBOLIC or LANGUAGE) to prevent the files from being interpreted by VS. – Phil Jul 29 '15 at 13:35
  • 2
    Unless it's a bug, within the source_group call, "/" slash behaves differently than "\\", at least w/rt Visual Studio. Using "/" produces a single top-level filter called, e.g., "top/middle/bottom" whereas "\\" gives "top" containing "middle" containing "bottom" hierarchically. – qdin Nov 26 '17 at 08:19
8

There is a new command since CMake 3.8 to automatically filter your files according to the folder structure relative to a certain path.

Here is a small example where the SRC_BUILD_FILES is a set containing all the files you want to filter. The filtering occurs relative to a certain base path, which is set using CMAKE_CURRENT_SOURCE_DIR in this example, but it can be any folder. The PREFIX name (here: Implementation) can be set to your liking and will contain the filtered set.

    source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}"
         PREFIX "Implementation"
         FILES ${SRC_BUILD_FILES})

So there is no need for globbing or manually re-iterating folder names in your cmake script.

StarShine
  • 1,940
  • 1
  • 27
  • 45
0

In modern CMake, where you primarily work with targets the following function may be useful:

function (target_source_group)
  set (_options
    GROUP_INTERFACE_SOURCES
    )
  set (_multi_value_args
    # Required
    TARGET
    ROOT_DIR
    )
  set (_one_value_args
    PREFIX
    )

  cmake_parse_arguments (i
    "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})

  # Check inputs

  foreach (_target IN LISTS i_TARGET)
    if (i_GROUP_INTERFACE_SOURCES)
      get_target_property (_target_sources ${_target} INTERFACE_SOURCES)
    else ()
      get_target_property (_target_sources ${_target} SOURCES)
    endif ()

    # Remove sources to be installed
    set (_source_to_install_regex
      "(\\$<INSTALL_INTERFACE:([^>;<$]+)>)")

    string (REGEX REPLACE
      "${_source_to_install_regex}"
      ""
      _sources_to_build
      "${_target_sources}")

    # Remove remaining ";"s. It seems safer to do it this way rather than include
    # them in _source_to_install_regex
    string (REGEX REPLACE
      "[;]+"
      ";"
      _sources_to_build
      "${_sources_to_build}")

    # Extract sources to be built
    set (_source_to_build_regex
      "\\$<BUILD_INTERFACE:([^>;<$]+)>")

    string (REGEX REPLACE
      "${_source_to_build_regex}"
      "\\1"
      _sources_to_build
      "${_sources_to_build}")

    foreach (_root IN LISTS i_ROOT_DIR)
      set (_sources_under_root_regex
        "${_root}/[^>;<$]+")

      string (REGEX MATCHALL
        "${_sources_under_root_regex}"
        _sources_under_root
        "${_sources_to_build}")

      source_group (
        TREE    "${_root}"
        FILES   ${_sources_under_root}
        PREFIX  "${i_PREFIX}"
        )
    endforeach ()
  endforeach ()
endfunction (target_source_group)