target_include_directories prior to 2.8.12?

2019-01-26 19:01发布

问题:

Our CMakeList.txt had a target_include_directories creep in. It broke our downlevel clients, like Ubuntu LTS, CentOS and Solaris.

I know I can guard it with the following (thanks ZW), but I need something for Cmake versions 2.8.11 and earlier (the ???).

cmake_minimum_required(VERSION 2.8.5 FATAL_ERROR)
...

if (NOT CMAKE_VERSION VERSION_LESS 2.8.12)
  target_include_directories(...)
else()
  ???
endif()

Our directory structure is rather simple. There's one directory with header files in them, and the same directory has the source files in them. This is by design so users don't have troubles under a debugger.

Is it OK to use the following given our configuration (its not clear to me if @steveire answer applies to us):

if (NOT CMAKE_VERSION VERSION_LESS 2.8.12)
  target_include_directories(...)
else()
  include_directories("${ROOT_SOURCE_DIR}")
endif()

If not, what should be used in place of target_include_directories for 2.8.11 and earlier?

回答1:

Problem

The target_include_directories() command has extended functionality compared to include_directories(). So I agree with @Tsyvarev that it mainly depends on how you use target_include_directories().

Solution

If you use target_include_directories(my_target PRIVATE ...) the equivalent would be set_property(TARGET my_target APPEND PROPERTY INCLUDE_DIRECTORIES ...). But include_directories() in contrast does set those directories for all targets in the current CMakeLists.txt and all of it's add_subdirectory() siblings.

If you use target_include_directories(my_target INTERFACE/PUBLIC ...) there is no such functionality in CMake prior to version 2.8.11. You can simulate the behavior of self-propagating include directories, but that's much more complex.

Recommendation

Don't use the new command if you still want to support older versions of CMake. Because this not just target_include_directories() (see CMake Version Compatibility Matrix/Commands).

References

  • Determine appropriate version for cmake_minimum_required()
  • [CMake] Determining appropriate setting for CMAKE_MINIMUM_REQUIRED
  • Is Cmake set variable recursive?

Backward Compatible target_include_directories()

The following code shows what older CMake versions would have to do to simulate target_include_directories():

function(target_include_directories _target)
    set_property(GLOBAL APPEND PROPERTY GLOBAL_TARGETS "${_target}")
    set(_mode "PRIVATE")
    foreach(_arg ${ARGN})
        if (_arg MATCHES "SYSTEM|BEFORE")
            message(FATAL_ERROR "target_include_directories: SYSTEM or BEFORE not supported")
        endif()
        if (_arg MATCHES "INTERFACE|PUBLIC|PRIVATE")
            set(_mode "${_arg}")
        else()
            get_filename_component(_inc_dir "${_arg}" ABSOLUTE)
            if (_mode MATCHES "PUBLIC|PRIVATE")
                set_property(TARGET ${_target} APPEND PROPERTY INCLUDE_DIRECTORIES "${_inc_dir}")
            endif()            
            if (_mode MATCHES "INTERFACE|PUBLIC")
                set_property(TARGET ${_target} APPEND PROPERTY MY_INTERFACE_INCLUDE_DIRECTORIES "${_inc_dir}")
            endif()            
        endif()
    endforeach()
endfunction(target_include_directories)

function(target_link_libraries _target)
    set_property(GLOBAL APPEND PROPERTY GLOBAL_TARGETS "${_target}")
    set(_mode "PUBLIC")
    foreach(_arg ${ARGN})
        if (_arg MATCHES "INTERFACE|PUBLIC|PRIVATE|LINK_PRIVATE|LINK_PUBLIC|LINK_INTERFACE_LIBRARIES")
            set(_mode "${_arg}")
        else()
            if (NOT _arg MATCHES "debug|optimized|general")
                set_property(TARGET ${_target} APPEND PROPERTY MY_LINK_LIBARIES "${_arg}")
            endif()
        endif()
    endforeach()
    _target_link_libraries(${_target} ${ARGN})
endfunction(target_link_libraries)

function(my_update_depending_inc_dirs _targets _target _dep_target)
    get_property(_libs TARGET ${_dep_target} PROPERTY MY_LINK_LIBARIES)
    if (NOT _libs)
        return()
    endif()
    foreach(_lib ${_libs})
        list(FIND _targets "${_lib}" _idx)
        if (NOT _idx EQUAL -1)
            get_property(_inc_dirs TARGET ${_lib} PROPERTY MY_INTERFACE_INCLUDE_DIRECTORIES)
            set_property(TARGET ${_target} APPEND PROPERTY INCLUDE_DIRECTORIES "${_inc_dirs}")
            # to prevent cyclic dependencies getting us into an endless loop
            # remove the target we already processed from the list
            list(REMOVE_AT _targets ${_idx})
            my_update_depending_inc_dirs("${_targets}" "${_target}" "${_lib}")
        endif()
    endforeach()
endfunction(my_update_depending_inc_dirs)

function(my_update_inc_dirs)
    get_property(_targets GLOBAL PROPERTY GLOBAL_TARGETS)
    list(REMOVE_DUPLICATES _targets)
    foreach(_target ${_targets})
        my_update_depending_inc_dirs("${_targets}" "${_target}" "${_target}")
    endforeach()
endfunction(my_update_inc_dirs)

Tested with the following code (NOTE: needs the my_update_inc_dirs() call at the end):

CMakeLists.txt

cmake_minimum_required(VERSION 2.6)

project(TargetIncludeDirectories)

...

add_library(PLib plib/source/plib.cpp)
target_include_directories(PLib PRIVATE plib/source PUBLIC plib/include)

add_library(Lib lib/source/lib.cpp)
target_include_directories(Lib PRIVATE lib/source PUBLIC lib/include)
target_link_libraries(Lib PRIVATE PLib)

add_executable(App main.cpp)
target_include_directories(App PRIVATE . PUBLIC include)
target_link_libraries(App PRIVATE Lib)

my_update_inc_dirs()