CMake and VisualStudio: Group files in solution explorer - c++

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

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.

Files exclusion (custom and transient) from build

I have many C++ (Google Test) source files in my Visual Studiosolution and I want to have the possibility to keep only a few for the build (to focus on the problem), but also to come back quick enough (two times by day) to the initial configuration.
We are using, more or less, about three solutions: Visual Studio, CMake and QT (but I could add yet another one). I never used QT, so the other two solutions I see are:
Visual Studio: folders are useless, but I can select files and exclude them from build. But these changes are saved in vcproj so I have to pay attention not to save them on the version control, which is annoying.
CMake: easy change the CMakeLists.txt (comment lines with the sources folders), but I always have the version control problem ... maybe I can configure the excluded files in a custom (user) file. Advantage: I can generate only what I want, more flexible and not so boring like the previous one.
By example, if I do not want the sources in src_2:
file(GLOB_RECURSE SRC_FILES_1
${SOURCE_BASE_DIR}/src_1/*.cpp
${SOURCE_BASE_DIR}/src_1/*.h
)
file(GLOB_RECURSE SRC_FILES_2
${SOURCE_BASE_DIR}/src_2/*.cpp
${SOURCE_BASE_DIR}/src_2/*.h
)
file(GLOB_RECURSE SRC_FILES_3
${SOURCE_BASE_DIR}/src_3/*.cpp
${SOURCE_BASE_DIR}/src_3/*.h
)
add_executable(${PROJECT_TEST_NAME}
${SRC_FILES_1}
# next line is commented
# ${SRC_FILES_2}
${SRC_FILES_3}
)
Is there another solution or a way to improve one of these proposed here?
You can control this with a cmake variable.
option(BUILD_TESTS "builds test cpp files" ON)
This adds an option for your cmake file. It defaults in this case to ON you can change that though. Now lets get on:
if(BUILD_TESTS)
set(TEST_CPP_FILES test1.cpp test2.cpp)
endif(BUILD_TESTS)
add_executabe(foo bar.cpp bar1.cpp $(TEST_CPP_FILES))
Here you define a variable with the source file of your tests (or whatever source files you want to build when the BUILD_TESTS is ON. These then get added to the target. If BUILD_TESTS is off this variable will be empty.
now to change the Value you can run
cmake <...> -DBUILD_TESTS=OFF
Or with ON if you want to turn them back on again. You also can keep the change in version control because it will default to ON and you need to explicitly disable it.
You can also exclude whole subdirectories or targets with this in the if statement if you not just only want to exclude source files.
EDIT:
For your example it could look like this:
if(BUILD_TESTS)
file(GLOB_RECURSE SRC_FILES_2
${SOURCE_BASE_DIR}/src_2/*.cpp
${SOURCE_BASE_DIR}/src_2/*.h
)
endif(BUILD_TESTS)
SRC_FILES_2 should be empty afterwards.

QtCreator CMake project - how to show all project files

I use QtCreator to open CMake project. Some directories apart from CMakeLists.txt contains only headers files *.h and for those directories QtCreator in the project tree view shows only CMakeLists.txt. How to fix that ? I need to see all project files from QtCreator.
Viewing project as a file system is not a solution at all cause your project editor settings for example would not apply.
And I do not like to add headers to executable target, cause they do not actually belong there. You effectively cripple the project file to work nicely with one particular IDE... not good.
The cleaner option IMHO would be:
FILE(GLOB_RECURSE LibFiles "include/*.hpp")
add_custom_target(headers SOURCES ${LibFiles})
As a bonus you get your includes shown in a separate folder.
(borrowed from https://cmake.org/pipermail/cmake/2012-August/051811.html)
I would suggest you switching your project view to File System. This would display a view where you could view any file you want:
You might want to split your project view into two by clicking the second to right button, if you still desire the Projects mode.
You should add header files to the list of your source files: add_executable(${Executable} ${Sources} ${headers})
You can use GLOB_RECURSE if have many header files:
FILE(GLOB_RECURSE INC_ALL "headers/*.h")
include_directories("headers")
add_executable(main "main.cpp" ${INC_ALL})
Don't forget to run CMake again (Build>Run Cmake).
Based on another thread asking the same question, I found a generic solution to the problem, working for all IDE's (at least tested with QtCreator and Visual Studio).
Can be found here : https://github.com/sauter-hq/cmake-ide-support
# \brief adds for the given target a fake executable targets which allows all
# headers and symbols to be shown in IDEs.
# \param target_name Which target properties should be added to the IDE support target.
function(target_add_ide_support target_name)
if (NOT TARGET ${target_name})
message(FATAL_ERROR "No target defined with name ${target_name}, cannot target_add_ide_support it.")
endif()
set (target_for_ide "${target_name}_ide_support")
if (NOT TARGET ${target_for_ide})
file(GLOB_RECURSE target_for_ide_srcs "*.h" "*.hpp" "*.hxx" "*.c" "*.cpp" "*.cxx")
add_executable(${target_for_ide} ${target_for_ide_srcs})
set_target_properties(${target_for_ide} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1)
endif()
get_target_property(dirs ${target_name} INCLUDE_DIRECTORIES)
target_include_directories(${target_for_ide} PRIVATE ${dirs})
endfunction(target_add_ide_support)
Usage is then for any targets in the CMakeLists, add the following call (can be made in top-most CMakeLists.txt after all add_subdirectory :
include(add_ide_support.cmake)
target_add_ide_support(some-target)
You can try CMakeProjectManager2. Code to display all files already propagated to upstream as a proof of concept. Concept applied but code can't be applied as-is for some reasons. So, simple wait feature in upstream.
There is a closed bug report about this issue: CMake project shows no files.
In that particular case the issue was with the chosen generator, Ninja, which is not well supported by QtCreator.
Please change that to "CodeBlocks - Ninja". Creator needs the CodeBlocks extra generator.
You should see a warning about that when hovering the kit (and the kit should have a warning icon in front of its name).
Using CodeBlocks - Ninja solved it for me too.
Overall, it may help to try up a few generators...

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