Cmake: How to Change Properties on Subdirectory Project Targets

CMake: How do I change properties on subdirectory project targets?

I've given your example a try and here are my two variants:

  1. Using the BUILDSYSTEM_TARGETS and SUBDIRECTORIES 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)
  2. By transforming the FOLDER target property into something that is inherited from a directory property of the same name. This can be done using define_property() to redefine the FOLDER property as INHERITED:

    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 to GLOBAL. TARGET, SOURCE, and TEST chain to DIRECTORY.

    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 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 to GLOBAL. TARGET, SOURCE, and TEST chain to DIRECTORY.

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, running make 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



Leave a reply



Submit