Get all source files a target depends on in CMake - cmake

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.

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
Recursive list of LINK_LIBRARIES in CMake

Related

why value of cmake function parameter is literal?

cmake_minimum_required(VERSION 3.9)
project(test)
set(MY_VAR "XXXXXX")
function(echo var)
message(STATUS "var = ${var}")
endfunction()
echo(MY_VAR) # output MY_VAR
why the output is MY_VAR, it should be XXXXXX, is cmake function parameter doesn't pass by value/reference but literal?
it seems that vairable in cmake function call is treated as string, it is a so wired design. when we need to read and modify a variable in function, we have to wirte in this ugly way?
set(MY_VAR "XXXXXX")
function(echo var)
message(STATUS "var = ${${var}}")
set(${var} "new value")
endfunction()
echo(MY_VAR)

CMake. Cann't get target output file name

I use CMake 3.10. Previously I've used 3.5.
According manual i can use $ to get some path to output file. In fact anything isn't printed.
Status is "-- lib_location == $" I looked into examples of
But if I use next construction, it works well.
add_custom_target(
testTartgetFile ALL
COMMAND ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:tgt1>"
VERBATIM
)
The question is how to get target object? I need it for further handling, not for print out.
My code:
cmake_minimum_required(VERSION 3.0)
project(libtest_project)
function(add_txbundle)
set(options NONE)
set(oneValueArgs TARGET)
set(multiValueArgs EXTRA_MAPPINGS DEPENDENCIES)
set(txPrefix "TxBundle")
cmake_parse_arguments(${txPrefix} "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
message(status " TARGET ${${txPrefix}_TARGET}")
message(status " EXTRA_MAPPINS ${TxBundle_EXTRA_MAPPINGS}")
set(TxBundleTarget "${${txPrefix}_TARGET}.txbundle")
set(TxParentTarget "${${txPrefix}_TARGET}")
message(status " TX TARGET ${TxBundleTarget}")
#..... some actions ...
endfunction(add_txbundle)
add_library(testlb SHARED testlib.cpp)
message (STATUS "lib_location == $<TARGET_FILE:testlb>")
add_txbundle(TARGET testlb EXTRA_MAPPINGS "1:1")

How to add empty element to CMake list?

There is CMake script:
unset(FOO)
list(APPEND FOO "")
list(APPEND FOO "")
list(APPEND FOO "")
list(APPEND FOO "")
list(LENGTH FOO SIZE)
message(FATAL_ERROR ${SIZE})
Expectation - 4. Reality - 0
I've found a workaround, but it looks like a kludge:
set(FOO "dummy")
set(FOO "${FOO};")
set(FOO "${FOO};")
set(FOO "${FOO};")
set(FOO "${FOO};")
list(REMOVE_AT FOO 0)
message(FATAL_ERROR ${SIZE})
The example a little bit simpler than my task, so, please, don't suggest writing set(FOO ";;;"):) I get elements from outside, and some of them may be empty. Something like this:
function(do_smth LIST_WITH_EMPTY_ITEMS)
unset(RESULT)
foreach(X IN LISTS LIST_WITH_EMPTY_ITEMS)
if(${CONDITION})
list(APPEND RESULT "${X}")
endif()
endforeach()
endfunction()
It doesn't seem to work with empty lists, but if you initialize the list with some dummy value, as you did in your second example set(FOO "dummy"), you can continue to use FOO as a list and also append empty elements to it. It's basically your desired approach and workaround combined:
set(FOO "dummy")
list(APPEND FOO "")
list(APPEND FOO "")
list(APPEND FOO "")
list(APPEND FOO "")
list(POP_FRONT FOO)
list(LENGTH FOO SIZE)
message("content: (${FOO})")
message("sizt: ${SIZE}")
The output of this is:
content: (;;;)
size: 4

search a list of path in make for include_directories recursively

I'd like implement a function in cmake to search a list of path recursively.
Say given a search_paths: /a/b, /c, /d/e. I want to have a return_list with paths of directories under given search_paths which contains .h files. Here is my function:
FUNCTION(R_SEARCH search_paths return_list)
FOREACH(search_path ${search_paths})
# POINT A
R_SEARCH_INNER(${search_path} inner_return)
SET(all_list ${all_list} ${inner_return})
ENDFOREACH()
SET(${return_list} ${all_list})
ENDFUNCTION(R_SEARCH)
FUNCTION(R_SEARCH_INNER search_path return_list)
FILE(GLOB_RECURSE new_list ${search_path} *.h)
SET(dir_list "")
FOREACH(file_path ${new_list})
GET_FILENAME_COMPONENT(dir_path ${file_path} PATH)
SET(dir_list ${dir_list} ${dir_path})
ENDFOREACH()
LIST(REMOVE_DUPLICATES dir_list)
SET(${return_list} ${dir_list})
ENDFUNCTION(R_SEARCH_INNER)
based on http://public.kitware.com/pipermail/cmake/2012-June/050674.html
However, the inner function call on POINT A will only happen once.
Could anybody give me some hints or provide a workable solution.
You should change this:
FILE(GLOB_RECURSE new_list ${search_path} *.h)
into
FILE(GLOB_RECURSE new_list ${search_path}/*.h)
See documentation:
Examples of recursive globbing include:
/dir/*.py - match all python files in /dir and subdirectories
Also, as already suggested (see also here), change:
SET(${return_list} ${all_list})
[...]
SET(${return_list} ${dir_list})
into:
SET(${return_list} ${all_list} PARENT_SCOPE)
[...]
SET(${return_list} ${dir_list} PARENT_SCOPE)
So the corrected code becomes:
FUNCTION(R_SEARCH search_paths return_list)
FOREACH(search_path ${search_paths})
# POINT A
R_SEARCH_INNER(${search_path} inner_return)
SET(all_list ${all_list} ${inner_return})
ENDFOREACH()
SET(${return_list} ${all_list} PARENT_SCOPE)
ENDFUNCTION(R_SEARCH)
FUNCTION(R_SEARCH_INNER search_path return_list)
FILE(GLOB_RECURSE new_list ${search_path}/*.h)
SET(dir_list "")
FOREACH(file_path ${new_list})
GET_FILENAME_COMPONENT(dir_path ${file_path} PATH)
SET(dir_list ${dir_list} ${dir_path})
ENDFOREACH()
LIST(REMOVE_DUPLICATES dir_list)
SET(${return_list} ${dir_list} PARENT_SCOPE)
ENDFUNCTION(R_SEARCH_INNER)

CMake's STREQUAL not working

According to the CMake documentation, the STREQUAL comparison is allowed to take either a VARIABLE or a STRING as either parameter. So, in this example below, the message does NOT print, which is broken:
set( FUBARTEST "OK" )
if( FUBARTEST STREQUAL "OK" )
message( "It Worked" )
endif()
Any reason why this isn't working as documented?
The issue was my cache. I deleted my cache and reconfigured and now the code works.
I didn't test your example at first, but when I did, I see your code works fine on cmake 2.8.0, and the other combinations advertised in the docs do too:
set( FUBARTEST "OK" )
if( FUBARTEST STREQUAL "OK" )
message( "FUBARTEST Worked" )
else()
message( "FUBARTEST FAILED" )
endif()
set( FOO "OK" )
if( ${FOO} STREQUAL "OK" )
message("string STREQUAL string works" )
else ()
message("string STREQUAL string FAILED" )
endif()
set( FOO "OK" )
set( BAR "OK" )
if( FOO STREQUAL BAR )
message("variable STREQUAL variable works" )
else ()
message("variable STREQUAL variable FAILED" )
endif()
set( FOO "OK" )
if( FOO STREQUAL "OK" )
message("variable STREQUAL string works" )
else ()
message("variable STREQUAL string FAILED" )
endif()
gives output:
FUBARTEST Worked
string STREQUAL string works
variable STREQUAL variable works
variable STREQUAL string works
The same thing happens when using ' instead of " for the string comparison
This won't work:
if( FUBARTEST STREQUAL 'OK' )
message( "It Worked" )
endif()
This works (except if there is a cache issue like mentioned above):
if( FUBARTEST STREQUAL "OK" )
message( "It Worked" )
endif()
I had such problem on Windows, when on Linux all worked fine.
Turns out issue was, that on Linux logical operations can be written before cmake_minimum_required and project, when on Windows they must be placed after.
Working example:
cmake_minimum_required( VERSION 3.16 )
project( "strequal_example" VERSION 1.0 LANGUAGES C CXX )
if ( ${CMAKE_BUILD_TYPE} STREQUAL "Debug" )
message( STATUS "DEBUG mode enabled" )
endif()
And this example works only on Linux with CMake 3.16, but not on Windows:
if ( ${CMAKE_BUILD_TYPE} STREQUAL "Debug" )
message( STATUS "DEBUG mode enabled" )
endif()
cmake_minimum_required( VERSION 3.16 )
project( "strequal_example" VERSION 1.0 LANGUAGES C CXX )