Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have following structure

Main (dir)
      +-- CMakeLists.txt
      +-- File.cpp
      +-- File.hpp
      +-- Dir (dir)
          +-- CMakeLists.txt
          +-- File1.cpp
          +-- File1.hpp
          +-- File2.cpp
          +-- File2.hpp

Main/CMakeLists.txt

CMAKE_MINIMUM_REQUIRED (VERSION 2.8.11)
PROJECT(Main)
FILE(GLOB SOURCE
    "*.hpp"
    "*.cpp"
)

ADD_SUBDIRECTORY(Dir)
ADD_EXECUTABLE(Main ${SOURCE})

Main/Dir/CmakeLists.txt

FILE(GLOB LOCAL_SOURCE
    "*.hpp"
    "*.cpp"
)
SET(SOURCE
    ${SOURCE}
    ${LOCAL_SOURCE}
    PARENT_SCOPE
)

It generated the following structure in Visual Studio
enter image description here

What I want:

enter image description hereenter image description here

What I tried:

Main/CMakeLists.txt

CMAKE_MINIMUM_REQUIRED (VERSION 2.8.11)
PROJECT(Main)
FILE(GLOB LOCAL_SOURCE
    "*.hpp"
    "*.cpp"
)

SET(SOURCE 
    ${LOCAL_SOURCE}
)

ADD_SUBDIRECTORY(Dir)

SOURCE_GROUP(Main FILES ${LOCAL_SOURCE})
ADD_EXECUTABLE(Main ${SOURCE})

Main/Dir/CmakeLists.txt

FILE(GLOB LOCAL_SOURCE
    "*.hpp"
    "*.cpp"
)
SET(SOURCE
    ${SOURCE}
    ${LOCAL_SOURCE}
    PARENT_SCOPE
)

SOURCE_GROUP(Dir FILES ${LOCAL_SOURCE})

What I get:

enter image description here

Please help me regarding this.

  • I do not want to use single CmakeFile.txt in Main directory having filters
  • Actual structure is many layers deep nesting structure. So please suggest the solution which will work for any level sub directory
like image 771
Jai Avatar asked Jul 15 '15 06:07

Jai


4 Answers

There are several ready to use or adaptable solutions out there to mimic a Source Tree behavior like in Eclipse with CMake for Visual Studio (e.g. ADD_SRC_SUBFOLDER DESTINATION_SRCS from Zobra or GroupSources from Luca).

Here is my reduced version for your use case:

cmake_minimum_required(VERSION 2.8.10)

project(Main CXX)

set(
    source_list
    "File.cpp"
    "File.hpp"
    "Dir/File1.cpp"
    "Dir/File1.hpp"
    "Dir/File2.cpp"
    "Dir/File2.hpp"
)

add_executable(Main ${source_list})

foreach(source IN LISTS source_list)
    get_filename_component(source_path "${source}" PATH)
    string(REPLACE "/" "\\" source_path_msvc "${source_path}")
    source_group("${source_path_msvc}" FILES "${source}")
endforeach()

See the documentation of source_group() that you have to give the sub-directories with double backslashes.

For the reason why I replaced your file(GLOB ...) with a dedicated list of all source files I like to quote from CMake's file() command documentation:

We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate.

And here is my fail-safe version (that checks for absolute paths) to be used as a function:

function(assign_source_group)
    foreach(_source IN ITEMS ${ARGN})
        if (IS_ABSOLUTE "${_source}")
            file(RELATIVE_PATH _source_rel "${CMAKE_CURRENT_SOURCE_DIR}" "${_source}")
        else()
            set(_source_rel "${_source}")
        endif()
        get_filename_component(_source_path "${_source_rel}" PATH)
        string(REPLACE "/" "\\" _source_path_msvc "${_source_path}")
        source_group("${_source_path_msvc}" FILES "${_source}")
    endforeach()
endfunction(assign_source_group)

Which you would call in the example with

assign_source_group(${source_list})
like image 157
Florian Avatar answered Nov 06 '22 23:11

Florian


As of CMake 3.8, the source_group command offers a TREE argument to recursively search the files paths of your sources, and structures the source groups to match your file system structure. Now, this offers a much cleaner solution:

project(Main)

set(SOURCE_LIST
    "File.cpp"
    "File.hpp"
    "Dir/File1.cpp"
    "Dir/File1.hpp"
    "Dir/File2.cpp"
    "Dir/File2.hpp"
)

add_executable(Main ${SOURCE_LIST})

# Create the source groups for source tree with root at CMAKE_CURRENT_SOURCE_DIR.
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_LIST})
like image 45
Kevin Avatar answered Nov 06 '22 21:11

Kevin


I wanted to comment on https://stackoverflow.com/users/3987854/squareskittles 's answer, but I couldn't due to the lack of "reputation" ?

Anyway,

source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_LIST})

works like a charm, but I also needed to set this:

set_property(GLOBAL PROPERTY USE_FOLDERS ON)
like image 5
sin Avatar answered Nov 06 '22 22:11

sin


I know that using the CMAKE glob function is usually frowned upon: Why is CMAKE glob evil, but in my case I found it to better than explicitly naming each file. I figured I would include a modified version of Florian's answer using GLOB.

# This code sorts the project files as they appear in the root directory

# Generate a list of all .c & .h files in the current directory and sub directores.
file(
     GLOB_RECURSE source_list RELATIVE
     "${CMAKE_CURRENT_SOURCE_DIR}"
     *.c *.h
    )
foreach(source IN LISTS source_list)
    get_filename_component(source_path "${source}" PATH)
    string(REPLACE "/" "\\" source_path_msvc "${source_path}")
    source_group("${source_path_msvc}" FILES "${source}")
endforeach()  
message(STATUS "Tree reorganized")
like image 1
joeshmo Avatar answered Nov 06 '22 23:11

joeshmo