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

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.

Related

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

How to keep source folders hierarchy on solution explorer?

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

CMake -- Add all sources in subdirectory to cmake project

As a follow up to this question:
Add Source in a subdirectory to a cmake project
What is the best way (perhaps using the FILE directive?) to select all the .cpp and .h files in the subdirectory and add them to the SOURCE variable defined in the parent directory?
Example from answer to question above:
set(SOURCE
${SOURCE}
${CMAKE_CURRENT_SOURCE_DIR}/file1.cpp
${CMAKE_CURRENT_SOURCE_DIR}/file2.cpp
PARENT_SCOPE
)
set(HEADERS
${HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/file1.hpp
${CMAKE_CURRENT_SOURCE_DIR}/file2.hpp
PARENT_SCOPE
)
Is it possible to do something like this?
FILE(GLOB SUB_SOURCES *.cpp)
set(SOURCE
${SOURCE}
${CMAKE_CURRENT_SOURCE_DIR}/${SUB_SOURCES}
PARENT_SCOPE
)
What is the best way (using CMake) to compile all the sources in a directory and a subdirectory into a single output file (not multiple libraries?)
I think what you are looking for is the aux_source_directory command.
aux_source_directory Find all source files in a directory.
aux_source_directory( )
Collects the names of all the source files in the specified directory
and stores the list in the provided. This command is
intended to be used by projects that use explicit template
instantiation. Template instantiation files can be stored in a
"Templates" subdirectory and collected automatically using this
command to avoid manually listing all instantiations.
It is tempting to use this command to avoid writing the list of source
files for a library or executable target. While this seems to work,
there is no way for CMake to generate a build system that knows when a
new source file has been added. Normally the generated build system
knows when it needs to rerun CMake because the CMakeLists.txt file is
modified to add a new source. When the source is just added to the
directory without modifying this file, one would have to manually
rerun CMake to generate a build system incorporating the new file.
Your CMakeLists.txt within the subdirectory could look like this:
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SUB_SOURCES)
set(SOURCE
${SOURCE}
${SUB_SOURCES}
PARENT_SCOPE
)
The recommended practice is however, as you see from the documentation, to list the files individually within CMakeLists.txt as changes to the CMakeLists.txt file triggers running cmake.
I hope this was helpful and to the point.

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

Specify how cmake creates visual studio project

I'm setting up cmake for my project and I've set up a testing project for it. When it generates my visual studio 2010 project I want to make it as the project I've had earlier.
it creates a ALL_BUILD and ZERO_CHECK project that I dont want.
it puts the .h files into the External Dependencies folder. I want a Include Files folder where all the .h files goes.
I also want to group some files under different filters. Like in my core lib I want to group all files related to Maths in a folder and all files related to Event management in another.
On the filesystem it puts the project files inside /Lib/src. Probably cause I have it organized that in the code folder, but I dont want that for the project files.
I want to set up different configuration so I have DebugOpenGL, DebugDirectX, ReleaseOpenGL, ReleaseDirectX and then setting a flag USE_OPENGL or USE_DIRECTX for the two types of configurations.
How can I exclude some files when I build on windows and others when I build on linux? Like I have WindowWin.cpp and WindowLinux.cpp.
I've tried what you sugested but cant get it working:
#LibProject/src
FILE(GLOB test0_headers $CMakeTest_SOURCE_DIR/LibProject/inc/test.h)
source_group(include0 FILES ${test0_headers})
FILE(GLOB test0_source ${CMakeTest_SOURCE_DIR}/LibProject/src/test.cpp)
source_group(source0 FILES ${test0_source})
FILE(GLOB test1_headers $CMakeTest_SOURCE_DIR/LibProject/inc/test1.h)
source_group(include1 FILES ${test1_headers})
FILE(GLOB test1_source ${CMakeTest_SOURCE_DIR}/LibProject/src/test1.cpp)
source_group(source1 FILES ${test1_source})
include_directories(${test0_headers} ${test1_headers})
add_library(LibProject ${test0_headers} ${test1_headers} ${test0_source} ${test
1_source})
I kind of got it working now.. only that I want sub folders for headers and source files inside the source group.
set(test_source0 ${CMakeTest_SOURCE_DIR}/LibProject/inc/test.h ${CMakeTest_SOURCE_DIR}/LibProject/src/test.cpp)
source_group(TEST FILES ${test_source0})
set(test_source1 ${CMakeTest_SOURCE_DIR}/LibProject/inc/test2.h ${CMakeTest_SOURCE_DIR}/LibProject/src/test2.cpp)
source_group(TEST2 FILES ${test_source1})
include_directories(${CMakeTest_SOURCE_DIR}/LibProject/inc)
add_library(LibProject ${test_source0} ${test_source1})
Heres my solution :)
set(test_header
${CMakeTest_SOURCE_DIR}/LibProject/inc/test.h)
set(test_source
${CMakeTest_SOURCE_DIR}/LibProject/src/test.cpp)
source_group(TEST\\Headers FILES ${test_header})
source_group(TEST\\Source FILES ${test_source})
set(test2_header
${CMakeTest_SOURCE_DIR}/LibProject/inc/test2.h)
set(test2_source
${CMakeTest_SOURCE_DIR}/LibProject/src/test2.cpp)
source_group(TEST2\\Headers FILES ${test2_header})
source_group(TEST2\\Source FILES ${test2_source})
include_directories(${CMakeTest_SOURCE_DIR}/LibProject/inc)
add_library(LibProject
${test_header}
${test_source}
${test2_header}
${test2_source})
To group files into a folder in Visual Studio you can use:
# Include files
FILE(GLOB all_headers "include/*.h*")
source_group("include" FILES ${all_headers})
# Source files
FILE(GLOB all_srcs "src/*.cpp")
source_group("source" FILES ${all_srcs})
This will put all of your .h files that are located in the include folder to appear in a folder called include in Visual Studio. Same for your cpp files. You can use this technique to make your own structure in CMake, if that is what you want.
For the purposes of ALL_BUILD and ZERO_CHECK read this answer. To disable the generation of ZERO_CHECK use set(CMAKE_SUPPRESS_REGENERATION true).
To select between OpenGL and DirectX and to setup the appropriate folders, you can use the same technique used above but put them inside a
if(${USE_OPENGL})
FILE(GLOB ....)
endif()
I think you get the idea.
I'll answer the ones I know:
The ALL_BUILD and ZERO_CHECK are used to keep your projects in sync with changes to the CMakeFiles. I don't think there's a way to get rid of those.
To get the .h files where you want them, you need to include them as part of the source. I use something like this:
file(GLOB LocalProjectIncludes "${yourheaderdirectory}/*.h")
Then I append ${LocalProjectIncludes} to the source files for the project. CMake will figure out that they're headers; it won't try to build them.