29

I have already looked around (StackOverflow and more) and I'm trying to use cmake to generate Visual Studio filters. I have the following folders:

src/math  
src/import  
src/ui  

I would like to generate the filters like above.
math: contains all the cpp & h files in src/math
import: contains all the cpp & h files in src/import
ui: contains all the cpp & h files in src/ui

I have tried several solutions, but none seems to work!!!

Here is the last version of the code in CMakeList.txt:

set(VD_SRC "${VisualDesigner_SOURCE_DIR}/src/visualdesigner")

file(GLOB_RECURSE SRC_UI
    "${VD_SRC}/ui/*.cpp", "${VD_SRC}/ui/*.h")
file(GLOB_RECURSE SRC_IMPORT
    "${VD_SRC}/import/*.cpp",
    "${VD_SRC}/import/*.h")

source_group("ui"            FILES ${SRC_UI})
source_group("import"        FILES ${SRC_IMPORT})

Any help is welcomed!

Kevin
  • 16,549
  • 8
  • 60
  • 74
ClubberLang
  • 1,624
  • 3
  • 21
  • 45
  • I suppose you want to get a target for the files? Or what do you mean with filters? Is this something Visual Studio specific? – usr1234567 Nov 19 '15 at 16:03
  • Yes, Visual Studio has some kind of "folder" in the "solution", they call this "filter". It allow to organise all the files like the folders in VS ! – ClubberLang Nov 19 '15 at 16:38

5 Answers5

38

See How to set Visual Studio Filters for nested sub directory using cmake

Just be aware that

  • the source_group() command only works in combination with add_library() or add_executable() commands listing the same sources (the paths must match)
  • the source_group() command does not check if the file actually exists (so it takes anything you give it and during project file generation it tries to match the given source group file names against files used in the project)

I have given your code a try by adding a corresponding add_library() target and it works as expected (CMake 3.3.2 and VS2015):

set(VD_SRC "${VisualDesigner_SOURCE_DIR}/src/visualdesigner")

file(GLOB_RECURSE SRC_UI
    "${VD_SRC}/ui/*.cpp"
    "${VD_SRC}/ui/*.h"
)
file(GLOB_RECURSE SRC_IMPORT
    "${VD_SRC}/import/*.cpp"
    "${VD_SRC}/import/*.h"
)

add_library(VisalDesigner ${SRC_UI} ${SRC_IMPORT})

source_group("ui"            FILES ${SRC_UI})
source_group("import"        FILES ${SRC_IMPORT})

Results in

Solution Explorer with Filters

Here is a more generalized version taken from Visual Studio as an editor for CMake friendly project:

set(_src_root_path "${VisualDesigner_SOURCE_DIR}/src/visualdesigner")
file(
    GLOB_RECURSE _source_list 
    LIST_DIRECTORIES false
    "${_src_root_path}/*.c*"
    "${_src_root_path}/*.h*"
)

add_library(VisualDesigner ${_source_list})

foreach(_source IN ITEMS ${_source_list})
    get_filename_component(_source_path "${_source}" PATH)
    file(RELATIVE_PATH _source_path_rel "${_src_root_path}" "${_source_path}")
    string(REPLACE "/" "\\" _group_path "${_source_path_rel}")
    source_group("${_group_path}" FILES "${_source}")
endforeach()
Community
  • 1
  • 1
Florian
  • 39,996
  • 9
  • 133
  • 149
4

I found it easier to do this and thought it might be helpful to others. Make sure you are using the latest version of CMAKE.

file(GLOB_RECURSE _source_list *.cpp* *.h* *.hpp*)
foreach(_source IN ITEMS ${_source_list})
    get_filename_component(_source_path "${_source}" PATH)
    string(REPLACE "${CMAKE_SOURCE_DIR}" "" _group_path "${_source_path}")
    string(REPLACE "/" "\\" _group_path "${_group_path}")
    source_group("${_group_path}" FILES "${_source}")
endforeach()
Langerz
  • 71
  • 1
  • 5
  • This works really well! I used it with Visual Studio 2017 and CMake 3.8 – BenjaFriend Feb 06 '19 at 16:18
  • Using cmake 3.14.1 and Visual Studio 2019, this worked, _except I had to add the headers to `add_exectuable` or `add_library`_. If I didn't do that, only the .cpp files showed up in the filters/VS project. I couldn't find anything about this in the [docs](https://cmake.org/cmake/help/v3.14/command/source_group.html). Maybe a bug or maybe I'm missing something. – sg_man Apr 30 '19 at 21:16
3

As of CMake 3.8, the source_group command offers a TREE argument to recursively search the files paths of your sources to structure the source groups to match your file system structure. Now, the solution is a lot cleaner, no need for looping:

set(VD_SRC "${VisualDesigner_SOURCE_DIR}/src/visualdesigner")
file(GLOB_RECURSE UI_IMPORT_MATH_SRCS
    "${VD_SRC}/ui/*.cpp"
    "${VD_SRC}/ui/*.h"
    "${VD_SRC}/import/*.cpp"
    "${VD_SRC}/import/*.h"
    "${VD_SRC}/math/*.cpp"
    "${VD_SRC}/math/*.h"
)

add_library(VisualDesigner ${UI_IMPORT_MATH_SRCS})

# Create the source groups for source tree with root at VD_SRC.
source_group(TREE ${VD_SRC} FILES ${UI_IMPORT_MATH_SRCS})

Also, check out the new PREFIX argument you can use with source_group if you find that useful.

Disclaimer: I advise against the use of GLOB (see here) wherever possible.

Kevin
  • 16,549
  • 8
  • 60
  • 74
  • 1
    With CMake 3.8, `source_group` with `TREE` [does not place the files that are at the "root" of the src into the "root" filter](https://stackoverflow.com/questions/69564143/how-can-i-cleanly-place-files-in-the-root-filter-in-visual-studio-using-cmake), it places them in `Source Files` and `Header Files` filters. I have yet to find a way to get this done "cleanly". – Vaillancourt Oct 14 '21 at 16:21
2

For those, who want to create them under "Header Files" and "Source Files":

You can use source_group. Here's a concrete example.

Suppose you have a directory structure like:

|-include
    | some.h
    |-sub
       | someother.h 
|-src
   | some.cpp
   |-sub
      |-someother.cpp

Collect the files (some people - including the documentation - discourages use of GLOB, but I leave that to you, you can list of them one by if you want to, though I find GLOB is just fine many times):

file(GLOB HEADER_FILES "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h")
file(GLOB HEADER_FILES_SUB "${CMAKE_CURRENT_SOURCE_DIR}/include/sub/*.h")
file(GLOB SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h")
file(GLOB SOURCE_FILES_SUB "${CMAKE_CURRENT_SOURCE_DIR}/src/sub/*.h")

# Setup your library or executable:
add_library(MY_LIB ${HEADER_FILES} ${HEADER_FILES_SUB}
                   ${SOURCE_FILES} ${SOURCE_FILES_SUB})

# Here's the important part ("Header Files" and "Source Files" are literals.)
source_group("Header Files\\sub" ${HEADER_FILES_SUB})
source_group("Source Files\\sub" ${SOURCE_FILES_SUB})
kovac
  • 4,945
  • 9
  • 47
  • 90
1

All of the above answers work in general. If you have multiple library targets and wish to create fancy filters for each of the targets, then you need to be a bit careful.

After quite a lot of headbanging, I figured you should keep the source_group call in the same CMakeLists.txt file where you did an add_library(target ..) or add_executable(target ..). You can cache (internal) the "source\header lists" anywhere and use it here.

Jaswant P
  • 83
  • 1
  • 8