How to keep source folders hierarchy on solution explorer? - c++

I made a C++ project on Linux, and I grouped source files in many directories to organize myself.
I was using CMake to compile, with one CMakeFiles.txt on each subdirectory.
srcs
|--folderA
| |--Toto.cpp
| |--Tata.cpp
|
|--folderB
| |--Foo.cpp
| |--Bar.cpp
[...]
Recently, I opened it with Visual Studio 2015, which found every source file, but just put the entire list on the "Source Files" folder of solution explorer.
Source Files
|--Toto.cpp
|--Tata.cpp
|--Foo.cpp
|--Bar.cpp
I plan to have a huge number of files, and it shall be soon difficult to find one.
Is there any way to explicitly tell it to respect the folder hierarchy on solution explorer?

Use the source_group command.
source_group(<name> [FILES <src>...] [REGULAR_EXPRESSION <regex>])
Defines a group into which sources will be placed in project files. This is intended to set up file tabs in Visual Studio. The options are:
FILES
Any source file specified explicitly will be placed in group . Relative paths are interpreted with respect to the current source directory.
REGULAR_EXPRESSION
Any source file whose name matches the regular expression will be placed in group .

#James Adkison is correct; source_group is what you want to use. As of CMake 3.8, the improved source_group command now offers a TREE argument to recursively search your source hierarchy to create source groups to match it. Here is a basic solution for the example you provided:
project(MyProj)
set(MyProj_SOURCES
"folderA/Toto.cpp"
"folderA/Tata.cpp"
"folderB/Foo.cpp"
"folderB/Bar.cpp"
)
add_executable(Main ${MyProj_SOURCES})
# Create the source groups for source tree with root at CMAKE_CURRENT_SOURCE_DIR.
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${MyProj_SOURCES})

Related

How can I cleanly place files in the "root" filter in Visual Studio using CMake?

We're using CMake to generate our Visual Studio solutions. Our project is a "whole" app consisting of header files together with the source files (.h/.cpp).
We like to have the source/header files well nested under the filters in Visual Studio (the "folders" in the Solution Explorer), the same way we see them in the Windows File Explorer.
So we came up with something like this to achieve this:
# ...the files are added to the project in another way.
#
# Group files under filters
#
file(GLOB source_files *.cpp *.h )
file(GLOB source_files_benchmark benchmark/*.cpp benchmark/*.h )
file(GLOB source_files_builder builder/*.cpp builder/*.h )
file(GLOB source_files_demo demo/*.cpp demo/*.h )
file(GLOB source_files_dashboard dashboard/*.cpp dashboard/*.h )
file(GLOB source_files_gsl gsl/* )
file(GLOB source_files_jsonUtil2 jsonUtil2/*.cpp jsonUtil2/*.h )
source_group( "" FILES ${source_files} )
source_group( "benchmark" FILES ${source_files_benchmark} )
source_group( "builder" FILES ${source_files_builder} )
source_group( "dashboard" FILES ${source_files_dashboard} )
source_group( "demo" FILES ${source_files_demo} )
source_group( "gsl" FILES ${source_files_gsl} )
source_group( "jsonUtil2" FILES ${source_files_jsonUtil2} )
(Please note that we're using "the evil" file GLOB to generate our lists. We're aware of the risks and we found out that they were less of an inconvenience than to add new files manually; I also don't think the way we get the file list is relevant here.)
This works and we have the nice project structure when we load the project in Visual Studio.
I though I could probably improve upon this and came up with this alternative solution:
# Nicely put the files into "filters" in Visual Studio. We used to do this
# manually but this way requires less work, overall.
# Get all the files CMake knows about.
get_target_property(local_app_sources ${my_target} SOURCES)
get_target_property(local_app_headers ${my_target} HEADERS)
set(local_app_files ${local_app_sources})
list(APPEND local_app_files ${local_app_headers})
# Create two lists, one we'll use for the "root", and one we'll use for the "in
# folders". More details below.
set(local_root_files ${local_app_files})
set(local_in_folder_files ${local_app_files})
list(FILTER local_root_files EXCLUDE REGEX ".*/.*")
list(FILTER local_in_folder_files INCLUDE REGEX ".*/.*")
# For some reasons, CMake will place the files that are at the "root" under the
# "Source Files" filter in Visual Studio, so we have moved those files to their
# own list. We use the source_group(TREE command to make a nice tree with the
# files that are under a folder, and we'll use the default version of the
# command to place the files that are at the root into an "empty string" folder
# (no folder), so that they'll be placed at the root.
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${local_in_folder_files})
source_group( "" FILES ${local_root_files} )
I'm happy that we don't have to add new folders manually anymore. However, I find it's still a bit verbose and I'm wondering if I could achieve the same with a single call to source_group(TREE, without the need to split the list and put the files that are at the "root" explicit into that folder.
How can I achieve this? What am I missing?
I've tried the "PREFIX" parameter but it just put everything into that folder (I end up with <prefix>/Source Files/src..), and we also don't need a prefix.
We're using CMake 3.8.something (I know, it's old and we should upgrade... we'll get to it, someday.)
Your first solution can be simplified by using REGULAR_EXPRESSION option of source_group instead of FILES option.
This will also get rid of the evil GLOB command.
source_group("benchmark" REGULAR_EXPRESSION "benchmark/.+\.(h|cpp)")
source_group("builder" REGULAR_EXPRESSION "builder/.+\.(h|cpp)")
...
See CMake Regex Specification.
As for the TREE option, your solution is pretty good.
With the following minor modification, I can get the filtering behaviour as you desire, where files are filtered according to their folder name, and the ones at the root folder has no filter instead of being listed under "Source Files".
get_target_property(_sources my_target SOURCES)
get_target_property(_headers my_target HEADERS)
set(_files ${_sources} ${_headers})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${_files})
Note that it is sometimes necessary to run CMake command in a clean build environment for VS filters to take effect.
I suspect that was the reason you had to apply a workaround to the TREE solution.

How could I match all subdirectories automatically with cmake

Provided my project is like this:
|--main.cpp
|-A--a1.cpp
| |-a2.cpp
|
|-B--b1.cpp
|-b2.cpp
|--CMakeLists.txt
How could I add all the *.cpp (e.g main.cpp, A/a1.cpp, A/a2.cpp, B/b1.cpp, B/b2.cpp) to a cmake variable SRC ? I hope I could only need one CMakeLists.txt. By the way, what if I need to exclude certain .cpps ?
As mentioned by Alexander, you can use file(GLOB_RECURSE myVar myRegex) to get all the files matching the myRegex into a myVar as a list. In order to exclude some you could play around with the myRegex, or you could filter the list with list(FILTER myVar <INCLUDE|EXCLUDE> REGEX <regular_expression>)
But note that adding another .cpp file to your project will not automatically be added to your target on rebuild. You will need to explicitly reconfigure your project for the changes to be made. Since CMake 3.12 there is also an option CONFIGURE_DEPENDS for file GLOBE and GLOBE_RECURSE, that will do the update on rebuild for you.
Here is the NOTE from CMake documentation:
Note 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. The CONFIGURE_DEPENDS flag may not work
reliably on all generators, or if a new generator is added in the
future that cannot support it, projects using it will be stuck. Even
if CONFIGURE_DEPENDS works reliably, there is still a cost to perform
the check on every rebuild.
It's what you want:
file(GLOB_RECURSE SRC *.cpp)
For more info: https://cmake.org/cmake/help/v3.0/command/file.html
For excluding you can write function.
Note: But I don't recommend to use 'glob'.
Better use 'set' with list of files.

CMake and VisualStudio: Group files in solution explorer

To finish a long coding session on a project, I wanted to test if my CPP project is compilable on an arrangement of OS'es.
I've been working in Win10 all the time. Compiles fine.
I've tried a Raspberry Pi. Compiles fine.
I re-download a seperate copy of my project to a Win10 client, run cmake-gui, and open the project: My folder structure in the solution explorer all gone.
So I started digging around, and apparently this structure is kept in CMakeLists.txt with the command source_group. So I start adding more source_groupings to my cmake lists, and for some reason my groupings won't take.
Example:
source_group("game\\entitysystem" FILES ${entitysystem_SRC}) // Existing grouping
source_group("game\\entitysystem\\components" FILES ${components_SRC}) // My new grouping
My glob would be this:
file(GLOB components_SRC
"game/components/*.h"
"game/components/*.cpp"
)
file(GLOB entitysystem_SRC
"game/entitysystem/*.h"
"game/entitysystem/*.cpp"
)
I do believe my GLOB's are correct since the new project-clone compiles fine. It's just that every part of the new structure in Visual Studio's Solution Explorer seems lost. Yes, I have cleared Cmake's cache and regenerated the project. Doesn't change it.
Original structure:
Cloned project structure:
Edit:
I did make a mistake in my source_group as in that it should not put components beneath entitysystem, but still, why aren't there any filters created in Visual Studio?
First, make sure you are setting set_property(GLOBAL PROPERTY USE_FOLDERS ON).
Second, it is not recommended to use GLOB to collect a list of source files. From the file(GLOB 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.
The recommended way to list project files is to add them by hand to CMakeLists.txt.
If you still want to GLOB, it looks like you want to mirror the directory structure in your source tree. You can use a macro such as this every place you define a library or executable to automatically sort them for you:
foreach(FILE ${SRCS})
# Get the directory of the source file
get_filename_component(PARENT_DIR "${FILE}" DIRECTORY)
# Remove common directory prefix to make the group
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" "" GROUP "${PARENT_DIR}")
# Make sure we are using windows slashes
string(REPLACE "/" "\\" GROUP "${GROUP}")
# Group into "Source Files" and "Header Files"
if ("${FILE}" MATCHES ".*\\.cpp")
set(GROUP "Source Files${GROUP}")
elseif("${FILE}" MATCHES ".*\\.h")
set(GROUP "Header Files${GROUP}")
endif()
source_group("${GROUP}" FILES "${FILE}")
endforeach()

Visual Studio 2010 project file filters

I have a complex C/C++ bunch of applications that I'm working on which is supposed to also be platform independent. So far, is UNIX/Windows compatible and it runs fine. However, maintaing this monster on VS2010 is a nightmare. I have the following file structure:
/
sources
lib1
include
...
src
...
lib2
include
...
src
...
app3
include
...
src
...
builders
cmake
...
make
...
VS2010
vs2010.sln
lib1
lib1.vcxproj
lib1.vcxproj.filters
lib2
lib2.vcxproj
lib2.vcxproj.filters
app3
app3.vcxproj
app3.vcxproj.filters
As we can see, because everything is platform independent, I had to completely separate the builders from sources. IMHO, that itself is a very good practice and it should be enforced by everyone :)
Here is the problem now... VS2010 is completely unusable when it comes to organize the include/sources files in filters. You have to do that manually by repeatedly doing "Add -> New Filter" followed by "Add -> Exiting Item". I have a VERY complex folder structure and files in each and every include folder. The task for creating the filters becomes a full day job. On the other hand, I could just drag the whole folder from Explorer onto the project inside VS2010 but it will put all header/source files in there without any filters, rendering it worthless: you can't possible search within 100 files for the right one without having some sort of hierarchy..
Question is:
Is VS2010 having some obscure way of importing a folder AND preserving the folder structure as filters? Looks to me that M$FT people who created VS2010 think that M$FT is the only animal in the jungle and you MUST pollute the sources folder with builders projects so you can leverage "show hiden files" to include them in the project along with the folder structure. That is absurd IMHO...
You are using CMake, so I advise you stick with only this. You can generate makefiles and VS2010 project files with it (at least). For VS, the generated files are a sln and a bunch of vxproj (one for each project in the CMake script).
In CMake file you can group files, using the command source_group. The filters will be automatically generated for vs according to the source groups. I don't know for other IDE like Code::Blocks or NetBeans.
If you want automatic grouping based on file path [Comment request]:
# Glob all sources file inside directory ${DIRECTORY}
file(GLOB_RECURSE TMP_FILES
${DIRECTORY}/*.h
${DIRECTORY}/*.cpp
${DIRECTORY}/*.c
)
foreach(f ${TMP_FILES})
# Get the path of the file relative to ${DIRECTORY},
# then alter it (not compulsory)
file(RELATIVE_PATH SRCGR ${DIRECTORY} ${f})
set(SRCGR "Something/${SRCGR}")
# Extract the folder, ie remove the filename part
string(REGEX REPLACE "(.*)(/[^/]*)$" "\\1" SRCGR ${SRCGR})
# Source_group expects \\ (double antislash), not / (slash)
string(REPLACE / \\ SRCGR ${SRCGR})
source_group("${SRCGR}" FILES ${f})
endforeach()

How can I build a C++ project with multiple interdependent subdirectories?

I have a C++ project where I've used directories as more of an organizational element -- the way one might use packages in Java or directories in PHP. Directories are not intended to be self-sufficient elements, but rather just a way of organizing the whole of the project and keeping me from being overwhelmed by sources. How can I construct my CMakeLists.txt files to deal with this? Making the directories libraries doesn't seem to fit here, since they are all interdependent and not intended to be used that way.
As a related issue, most of the examples I've seen of multiple subdirectories in CMake (and there aren't very many of those) have ignored or glossed over the issue of setting include_directories, which is something I've been having trouble with. Short of combing my source files to determine which file depends on which and in what directory, is there anyway to just set all directories under /src/ as potential include directories and let CMake work out which ones are actually dependent?
Here's an example structure:
--src
--top1
--mid1
--bot1
--src1.cpp
--hdr1.h
--bot2
--src2.cpp
--hdr2.h
--mid2
--bot3
--src3.cpp
--src4.cpp
--hdr3.h
--top2
--mid3
--src5.cpp
--hdr4.h
So on and so forth. How can I structure my CMakeLists.txt files to handle this sort of structure?
Since the directory structure in your project is just there to keep your files organized, one approach is to have a CMakeLists.txt that automatically finds all sources files in the src directory and also adds all directories as include directories that have a header file in them. The following CMake file may serve as a starting point:
cmake_minimum_required(VERSION 3.12)
project (Foo)
file (GLOB_RECURSE Foo_SOURCES CONFIGURE_DEPENDS "src/*.cpp")
file (GLOB_RECURSE Foo_HEADERS CONFIGURE_DEPENDS "src/*.h")
set (Foo_INCLUDE_DIRS "")
foreach (_headerFile ${Foo_HEADERS})
get_filename_component(_dir ${_headerFile} PATH)
list (APPEND Foo_INCLUDE_DIRS ${_dir})
endforeach()
list (REMOVE_DUPLICATES Foo_INCLUDE_DIRS)
add_executable(FooExe ${Foo_SOURCES})
target_include_directories(FooExe PRIVATE ${Foo_INCLUDE_DIRS})
The two file(GLOB_RECURSE ... commands determine the set of source and header files. The foreach loop computes the set of include directories from the list of all header files. The CONFIGURE_DEPENDS flags tells CMake to re-run the glob command at build time.
One drawback with computing the set of source files is that CMake will not automatically detect when new files are added to your source tree. You manually have to re-create your build files then.
Though #sakra gave a good answer to this question, I believe it is more proper to approach it more in depth.
We want to separate our code into modules and libraries for many reasons. Like code encapsulation, re usability, easier debugging etc. This idea would propagate in compiling process too.
In other word, we want to divide the compilation process into little compilation steps, each belong to one module. So every module must have its own compilation procedure. This is why we use one CMakeLists.txt file per directory. Hence every directory would have its own compilation commands and there would be one master CMakeLists.txt file in the root directory of your project.
Here is an example. Consider the following structure of a project:
src/
|
- main.cpp
|
_sum/
|
- sum.h
|
- sum.cpp
We would have one CmakeLists.txt Per directory. First directory is the root directory of the project which src/ folder is in it. here is content for that file:
cmake_minimum_required(VERSION 3.4)
project(multi_file)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "-Wall")
add_subdirectory(src)
Next CMakeLists.txt would located in src/ directory:
add_subdirectory("sum")
add_executable(out main.cpp)
target_link_libraries(out sum)
And the last one will be in the sum/ directory:
add_library(sum SHARED sum.cpp)
I hope this helps. I created a github repository in case you feel you need to see the code or you need further explanation.
I'm not an expert on CMake but since there are no other answers I'll take a look at the documentaton and give it a go. Organizing source and include files in different directories is pretty much the norm.
It looks like CMake allows you to give a list of include directories:
http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:include_directories
So something like:
include_directories("src/top1/mid1/bot1" "src/top1/mid1/bot2/" ... )
These are passed to the compiler so it can find the header files and will be passed for each of the source files. So any of your source files should be able to include any of the header files (which I think is what you're asking for).
Similar to that you should be able to list all your source files in the add_executable command:
add_executable(name "src/top1/mid1/bot1/src1.cpp" "src/top1/id1/bot2/src2.cpp" ...)
So this would be a naive way of getting everything to build. Each source file will be compiled and will look for headers in all those directories and then the object files will get linked together. Consider if there is any way of simplifying this such that you don't need so many include folders, maybe there are only a few common header files that need to be referenced by all source files. If things get more complex you can buiild sub-hierarchies into libraries etc. Also consider seperating source files and headers (e.g. in src and include).