10

With CMake, how can I get a list of all the source files which go into an executable target, including all sources in all targets this executable depends on?

We have a pattern in the code base where initializer callers are generated by the build system based on file names and paths in the source tree. So I need the full path (or relative to source root) to all source files an executable target depends on.

Petter Kvalvaag
  • 323
  • 2
  • 11
  • 2
    You could use the [`SOURCES`](https://cmake.org/cmake/help/latest/prop_tgt/SOURCES.html) target property and you can find [here](http://stackoverflow.com/questions/32756195/recursive-list-of-link-libraries-in-cmake) code samples that iterate over the depending targets. Can you please describe what you have tried so far? – Florian Sep 12 '16 at 11:22
  • I did not realize I could iterate over the depending targets like that. I think I can make it work with the SOURCES property. – Petter Kvalvaag Sep 12 '16 at 11:43
  • I've written something similar by overwriting CMake functions and could provide a code example if you are interested (as a starting point). Is this some external library CMake code we are talking about here or do you want to use it on your own code? – Florian Sep 12 '16 at 11:48
  • That would be great. This pertains to our own code only so anything is accessible. – Petter Kvalvaag Sep 12 '16 at 11:54
  • There is a related blog post from 2009, but still kind of clumsy: agateau.com/2009/cmake-and-make-dist – usr1234567 Jan 02 '17 at 17:37

1 Answers1

9

Here is my piece of code to get one target's link dependencies:

function(target_link_libraries _target)
    set(_mode "PUBLIC")
    foreach(_arg IN LISTS 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(GLOBAL APPEND PROPERTY GlobalTargetDepends${_target} ${_arg})
            endif()
        endif()
    endforeach()
    _target_link_libraries(${_target} ${ARGN})
endfunction()

function(get_link_dependencies _target _listvar)
    set(_worklist ${${_listvar}})
    if (TARGET ${_target})
        list(APPEND _worklist ${_target})
        get_property(_dependencies GLOBAL PROPERTY GlobalTargetDepends${_target})
        foreach(_dependency IN LISTS _dependencies)
            if (NOT _dependency IN_LIST _worklist)
                get_link_dependencies(${_dependency} _worklist)
            endif()
        endforeach()
        set(${_listvar} "${_worklist}" PARENT_SCOPE)
    endif()
endfunction()

For older CMake versions (prior to 3.4), you will need to replace the IN_LIST check with a list(FIND ...) call:

[...]
        list(FIND _worklist ${_dependency} _idx)
        if (${_idx} EQUAL -1)
            get_link_dependencies(${_dependency} _worklist)
        endif()
[...]

And here is the test code I've used:

cmake_minimum_required(VERSION 3.4)

project(GetSources)

cmake_policy(SET CMP0057 NEW)

[... include functions posted above ...]

file(WRITE a.cc "")
add_library(A STATIC a.cc)

file(WRITE b.cc "")
add_library(B STATIC b.cc)

file(WRITE main.cc "int main() { return 0; }")
add_executable(${PROJECT_NAME} main.cc)

target_link_libraries(B A)
target_link_libraries(${PROJECT_NAME} B)

get_link_dependencies(${PROJECT_NAME} _deps)
foreach(_dep IN LISTS _deps)
    get_target_property(_srcs ${_dep} SOURCES)
    get_target_property(_src_dir ${_dep} SOURCE_DIR)
    foreach(_src IN LISTS _srcs)
        message("${_src_dir}/${_src}")
    endforeach()
endforeach()

References

Community
  • 1
  • 1
Florian
  • 39,996
  • 9
  • 133
  • 149
  • Works great for executable targets. For some reason I needed to `cmake_policy(SET CMP0057 NEW)` for the `IN_LIST` to work inside the `if` statement when running this on a second library target. – Petter Kvalvaag Sep 14 '16 at 07:39
  • @PetterKvalvaag Thanks for the hint. I actually introduced the `IN_LIST` to the code for this answer and didn't know about policy `CMP0057` (CMake 3.4 I used for testing doesn't throw a warning/error). I've updated my answer accordingly and added the variant I had before (tested with CMake version 2.8). – Florian Sep 14 '16 at 10:30
  • Thank you. In my case also required next restriction: get_target_property(TARGET_TYPE ${_dep} TYPE) if (NOT TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") – tenta4 Dec 19 '19 at 15:31