Get SOURCES of Target

Hi,

for a function (see Static analyzers as seperate targets) I need all absolute path from source files of a target. This raises a couple of questions:

  • How do I get all sources of a target? → SOURCES Property is possible, but then the function can only be called after all sources have been declared (furthermore target_sources also supports generator expressions, so this won’t work in this case)
  • How do I only get direct sources of a target → Generator Expression with SOURCES solves the previous problem, but this contains then all sources from linked libraries with public sources
  • How do I get only absolute paths → Currently some are absolute and some relative, I only need absolute

Any solution for any of the problems? I tried experimenting a little bit with SOURCES, generator expression, some cmake logic, but so far I didn’t find any generic working approach

2 Likes

I don’t think CMake offers any official way to get this information. I suppose one could peruse compile_commands.json and gather files that have object outputs under a CMakeFiles/targetName.dir/ directory to filter those to a specific target.

The cmake-file-api is probably the best available today.

However, neither of these is immediately helpful for static analyzers. For that use case, I would suggest using compile_commands.json as the input to some tool that either runs the commands directly or can otherwise do what you need for each entry that is wanted.

I already use a compile_commands.json. I pass it to all my static analyzers. But the problem is that the compile_commands.json has all files in it. So I need to add filter to the static analyzers to only the entries belonging to the target.

So that’s what I basically try to do. Pass the source files to the static analyzer and the static analyer will then pick up the entry from the compile_commands.json

One entry:

{
  "directory": "<directory",
  "command": "<compiler> .... -o  some/path/someTarget.dir/someFile.o",
  "file": "/some/path/someFile.cpp"
},

So your suggestion, as far as I understood it, is that I query this file for an all the entries that contain -o .../myTarget.dir/.... For instance as a python script.
I guess that should be doable.

The cmake-file-api would be an alternative to python? How can it help me here? I had a look at it, but it seems not too easy to use.

Regarding an “official way” → There are basically two problems here:

  1. That SOURCES contains absolute and relative paths. The relative paths basically just need to be prepended with the target source directory, right? So I just need to add some logic to determine if a path is already absolute

https://cmake.org/cmake/help/latest/command/cmake_path.html#is-absolute

May I can even use https://cmake.org/cmake/help/latest/command/cmake_path.html#generation? This sounds like I can pass both relatives and absolute paths in it and it will just convert the relativ paths

  1. Getting all the source files. Currently I can use SOURCES already, but I need to call then my function at the end and cannot use generator expressions in the target_sources. Not nice, but kinda acceptable for now.
    Better would it be to use a generator expression myself, which solves both of these issues. But then SOURCES will contain also inherited source files.

→ Is this something CMake could improve? → I feel like in general it would be useful to have a seperation between inherited and directly specified sources / libraries / include paths? This would also help in other scenarios.
Is this something that could be added to cmake, so have for instance three variables SOURCES (as before) and addionally a SPECIFIED_SOURCES and possible INHERITED_SOURCES? (SOURCES would then be a merge of these two)?

The thing is that some sources cannot be known until generator time (e.g., genex conditionals), so any configure-time query will be insufficient for accurate behaviors.

Yes. You could at least filter out those not under the source or binary directories of the project I imagine.

If that works, sure. I don’t know how paths returned from SOURCES report if they are added in different directories.

Not even if I use a generator expression myself, so something like:

$<TARGET_PROPERTY:${target},SPECIFIED_SOURCES>

Is there like some order in which generator expressions are evaluated? So if I have:

target_sources(MyTarget $<...>)
$<TARGET_PROPERTY:${target},SPECIFIED_SOURCES>

Is it just the order in which they are, so the one in target sources is evaluated earlier?

Based on some Tests SOURCES contain relative paths for the sources in the same directory of the target (so basically just the file names) and absolute path for any subdirectories.
Which is weird to be honest :smiley:
I would expect all to be a relative path.

That’s fine, but you’ll only get the result at generate time. I suppose you could post-process this with knowledge of the base directory at build time into what you want, but there’s no genex-level way to guarantee you get a full path at the end (that would be configure-time logic). Something like:

set(source_root "@CMAKE_SOURCE_DIR@")
set(binary_root "@CMAKE_BINARY_DIR@")
set(target_root_dir "@target_source_dir@")
set(all_sources "$<TARGET_PROPERTY:@target@,SOURCES>")

foreach (source IN_LISTS sources)
  if (IS_RELATIVE "${source}")
    string(PREPEND source "${target_root_dir}/")
  endif ()
  # skip source if no under `source_root` or `binary_root`
endforeach ()

# Work with remaining sources
configure_file(
  "that_file_above.cmake.genex.in"
  "${CMAKE_CURRENT_BINARY_DIR}/that_file_above.cmake.genex"
  @ONLY)
file(GENERATE
  INPUT "${CMAKE_CURRENT_BINARY_DIR}/that_file_above.cmake.genex"
  OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/that_file_above.cmake")

in an API that does this in a target-specific way (namely what it is named at the end).