CMake: How do I change properties on subdirectory project targets?
I've given your example a try and here are my two variants:
Using the
BUILDSYSTEM_TARGETS
andSUBDIRECTORIES
directory properties to evaluate a list of target names in the directory that "does not include any Imported Targets or Alias Targets":cmake_minimum_required(VERSION 3.7)
project(AliasFolderSub)
set_property(GLOBAL PROPERTY USE_FOLDERS TRUE)
function(get_all_targets _result _dir)
get_property(_subdirs DIRECTORY "${_dir}" PROPERTY SUBDIRECTORIES)
foreach(_subdir IN LISTS _subdirs)
get_all_targets(${_result} "${_subdir}")
endforeach()
get_property(_sub_targets DIRECTORY "${_dir}" PROPERTY BUILDSYSTEM_TARGETS)
set(${_result} ${${_result}} ${_sub_targets} PARENT_SCOPE)
endfunction()
function(add_subdirectory_with_folder _folder_name _folder)
add_subdirectory(${_folder} ${ARGN})
get_all_targets(_targets "${_folder}")
foreach(_target IN LISTS _targets)
set_target_properties(
${_target}
PROPERTIES FOLDER "${_folder_name}"
)
endforeach()
endfunction()
# External Libs
add_subdirectory_with_folder("Poco" libs/poco)By transforming the
FOLDER
target property into something that is inherited from a directory property of the same name. This can be done usingdefine_property()
to redefine theFOLDER
property asINHERITED
:With the
INHERITED
option the get_property() command will chain up to the next higher scope when the requested property is not set in the scope given to the command.DIRECTORY
scope chains toGLOBAL
.TARGET
,SOURCE
, andTEST
chain toDIRECTORY
.cmake_minimum_required(VERSION 2.6)
project(AliasFolderSub)
set_property(GLOBAL PROPERTY USE_FOLDERS TRUE)
define_property(
TARGET
PROPERTY FOLDER
INHERITED
BRIEF_DOCS "Set the folder name."
FULL_DOCS "Use to organize targets in an IDE."
)
function(add_subdirectory_with_folder _folder_name _folder)
add_subdirectory(${_folder} ${ARGN})
set_property(DIRECTORY "${_folder}" PROPERTY FOLDER "${_folder_name}")
endfunction()
# External Libs
add_subdirectory_with_folder("Poco" libs/poco): Using
define_property()
to redefine an existing property's scope is an undocumented behavior of CMake.
References
- Directory properties and subdirectories
- "make dist" equivalent in CMake
- How to set Visual Studio Filters for nested sub directory using cmake
Directory properties and subdirectories
Turning my comment into an answer
If I look at CMake's source code this depends on the chained
member of cmPropertyDefinition
to be true.
So you can achieve this for your own directory property by using the INHERITED
keyword with define_property()
:
define_property(
DIRECTORY
PROPERTY narf
INHERITED
BRIEF_DOCS "Brief Doc"
FULL_DOCS "Full Doc"
)
Even if the INHERITED
documentation says only:
If the
INHERITED
option then theget_property()
command will chain up to the next higher scope when the requested property is not set in the scope given to the command.DIRECTORY
scope chains toGLOBAL
.TARGET
,SOURCE
, andTEST
chain toDIRECTORY
.
CMake install (TARGETS in subdirectories)
According to this bugreport, install(TARGETS)
command flow accepts only targets created within the same directory.
So you need either move the add_library()
call into the top-level directory, or split install(TARGETS)
call into per-target ones, and move each of them into the corresponding subdirectory.
Since CMake 3.13 install(TARGETS)
can work even with targets created in other directories.
install(TARGETS)
can install targets that were created in other directories. When using such cross-directory install rules, runningmake install
(or similar) from a subdirectory will not guarantee that targets from other directories are up-to-date.
How to set Visual Studio Filters for nested sub directory using cmake
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})
cmake variable scope, add_subdirectory
As mentioned in the documentation of the set command, each directory added with add_subdirectory
or each function declared with function
creates a new scope.
The new child scope inherits all variable definitions from its parent scope. Variable assignments in the new child scope with the set
command will only be visible in the child scope unless the PARENT_SCOPE
option is used.
To make the SOURCEFILES
assignment visible in the root folder of your project, try:
set (SOURCEFILES main.cpp foo.cpp PARENT_SCOPE)
Keeping file hierarchy across subdirectories in CMake
There are 3 ways I have used before. I normally prefer the 1st way, but have already used all 3 depending on the use case:
1. You directly name the sources in your root CMakeLists.txt
file
set(
SUBD1_SOURCES
"SubD1/SubSubD1/Source2.cpp"
"SubD1/Source.cpp"
)
set(
SUBD2_SOURCES
"SubD2/Source3.cpp"
)
add_executable(myProg ${SUBD1_SOURCES} ${SUBD2_SOURCES})
2. You use OBJECT
intermediate libraries to collect/group your sources
SubD1/SubSubD1/CMakeLists.txt:
add_library(SubSubD1Objs OBJECT Source2.cpp)
SubD1/CMakeLists.txt:
add_subdirectory(SubSubD1)
add_library(SubD1Objs OBJECT Source.cpp)
CMakeLists.txt:
add_executable(myProg $<TARGET_OBJECTS:SubSubD1Objs> $<TARGET_OBJECTS:SubD1Objs>)
3. You write your own function()
to collect the data (and do the prefixing)
CMakeLists.txt:
function(my_collect_sources)
foreach(_source IN ITEMS ${ARGN})
if (IS_ABSOLUTE "${_source}")
set(source_abs "${_source}")
else()
get_filename_component(_source_abs "${_source}" ABSOLUTE)
endif()
set_property(GLOBAL APPEND PROPERTY GlobalSourceList "${_source_abs}")
endforeach()
endfunction(my_collect_sources)
add_subdirectory(SubD1)
#add_subdirectory(SubD2)
get_property(MY_SOURCES GLOBAL PROPERTY GlobalSourceList)
add_executable(myProg ${MY_SOURCES})
SubD1/CMakeLists.txt:
add_subdirectory(SubSubD1)
my_collect_sources(Source.cpp)
SubD1/SubSubD1/CMakeLists.txt:
my_collect_sources(Source2.cpp)
How to add_subdirectory based on generator expression CONFIG rather than CMAKE_BUILD_TYPE?
Turning my comment into an answer
Since CMake needs to go through all your CMakeLists.txt
files during configuration step the answer is yes, you have to always call add_subdirectory()
.
I think what you are looking for is the EXCLUDE_FROM_DEFAULT_BUILD_<CONFIG>
target property.
Related Topics
Locating iOStream in Clang++: Fatal Error: 'iOStream' File Not Found
Checking If Pointer Points Within an Array
Access Processor Interrupts with C++ and X86 and X64 Architectures
Capturing Cout in Visual Studio 2005 Output Window
Gcc 7, -Wimplicit-Fallthrough Warnings, and Portable Way to Clear Them
What Is Double Evaluation and Why Should It Be Avoided
Xlib How Does This (Removing Window Decoration) Work
Compiling Multithread Code with G++ (-Wl,-No-As-Needed Not Working)
Setting Extra Bits in a Bool Makes It True and False at the Same Time
Volatile Struct = Struct Not Possible, Why
Dijkstra Graph with a Table of Weights on Each Edge
Capturing H264 Stream with Opencv
Performance of Std::Function Compared to Raw Function Pointer and Void* This
Is Returning Local Static Object Thread Safe for Pre-C++11 Compilers
Const Member and Assignment Operator. How to Avoid the Undefined Behavior
Ubuntu System Monitor and Valgrind to Discover Memory Leaks in C++ Applications