c++cmakepoco-librariesbuild-tools

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


I'm trying to organize the targets in my subproject (in this case poco), but I've come to find that properties can't be modified for ALIAS targets. I want the targets in my external project to be in their own folder, instead of sprawled out everywhere in the project tree (say the visual studio generator). Is there an easier way to add projects with my own properties?

So instead of:

- CMakePredefinedTargets
    - ALL_BUILD
    - INSTALL
    - ...
- MyTargets
    - SomeLibrary
    - SomeExe
- CppUnit
- Crypto
- Data
- ...

I want:

- CMakePredefinedTargets
    - ALL_BUILD
    - INSTALL
    - ...
- MyTargets
    - SomeLibrary
    - SomeExe
- Poco
    - CppUnit
    - Crypto
    - Data
    - ...

My attempt:

function(add_subdirectory_with_folder folder_name)
    function(add_library name type)
    _add_library(${ARGV})

    set_target_properties(${name}
        PROPERTIES
        FOLDER "${folder_name}"
    )
    endfunction()
    add_subdirectory(${ARGN})
endfunction()

# External Libs
add_subdirectory_with_folder("Poco" libs/poco)

Example target from the poco library:

add_library( "${LIBNAME}" ${LIB_MODE} ${SRCS} )
add_library( "${POCO_LIBNAME}" ALIAS "${LIBNAME}")
set_target_properties( "${LIBNAME}"
    PROPERTIES
    VERSION ${SHARED_LIBRARY_VERSION} SOVERSION ${SHARED_LIBRARY_VERSION}
    OUTPUT_NAME ${POCO_LIBNAME}
    DEFINE_SYMBOL JSON_EXPORTS
    )

My goal is to make it so I don't have to fork and maintain my own versions of libraries that I want to use just for quality of life tweaks. Is there a different method I can use to organize the project tree for IDEs? I know externalproject_add exists, but I do not think this has the facilities I am looking for. I will be adding other projects in the future in the form of git-submodules, but depending on if there is an easier method for this I'll explore other avenues.

EDIT:

To clarify, I'm already using a separate CMakeLists.txt for each module of my own project, plus a top level CMakeLists.txt which ties them all together, as well as collecting external libraries that my targets rely on. I want to modify the targets of external libraries without having to fork and maintain them myself just so I have nice folder structures in visual studio, xcode, or others. Linux obviously doesn't matter as much since most editing tools are folder based already.


Solution

  • 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