0

I'm trying to understand a good method for understanding expansive CMake projects (several layers of subdirectories) without having to be intimately familiar with every part of the project. In short I'd like to have some sort of code navigation ability for cmake files. This could potentially be something like ctags or VS Code's Intellisense's "Go To Definition" that can take some library name, (or generally some variable) and go to the definition of the library or variable. I'll clarify what I mean by "definition of the library" in the example below.

Here is a simple example, where I wouldn't need such a tool, but I hope makes the question more clear. Say I had a library module_a created by cmake in a file src/module_a/CMakeLists.txt:

add_library(module_a STATIC
    module_a.c
)

target_include_directories(module_a PUBLIC
    ${CMAKE_CURRENT_LIST_DIR}
)

and an executable that depends on this library, that is created via src/main/CMakeLists.txt:

add_executable(main
    main.c
)

target_link_libraries(main
    module_a
)

Is there any way for me to know what module_a is referencing within the src/main/CMakeLists.txt without already knowing about the file src/module_a/CMakeLists.txt? In other words I want to be able to look at a single CMakeLists.txt file within a larger project and have some tool be able to tell me where the variables, libraries, and other dependencies are defined.

For the above example, I'd like to open src/main/CMakeLists.txt and click something and jump to src/module_a/CMakeLists.txt on the add_library call in src/module_a/CMakeLists.txt. In this way I can see what I'm calling the "definition of the library".

If no such tool/plugin exists, what is there a good way to grep the output directory of cmake to find this information? I'm guessing the information is in there somewhere, but I'm just not sure what to look for and just a blanket grep -R module_a src/build even for this simple example is an overwhelming amount of information.

topher217
  • 1,188
  • 12
  • 35
  • Have you looked into `cmake --trace` and related options? – Friedrich Jun 20 '23 at 07:25
  • @Friedrich how would that solve this problem / answer this question? – starball Jun 20 '23 at 07:28
  • @starball not at all: it's a *comment* not an answer :-) Bear with me, `cmake --trace`'s output contains file names, line numbers, variables and targets. I haven't tried but a little scripting should turn it into a tags file - what the OP wanted. I find that many users of CMake are not familiar with the `--trace` option so I thought I'd ask. – Friedrich Jun 20 '23 at 07:33
  • @Friedrich from the limited docs, it says: "_Print a trace of all calls made and from where._". It doesn't say that it logs where those function are defined, which is an integral part of this question is it not? – starball Jun 20 '23 at 08:01
  • @starball please give `cmake --trace` a try and see what it does. It prints `file(line): cmake_code` and you would have to check if `cmake_code` is any of `set(foo ...`, `add_library(foo ...` or any of the other "definitions" you are interested in. These can be transformed into tags format as `foo file line;`. Discard the other traces. A script to do so is quickly written in `awk` or `sed`. The caveat is that the tags file would not be created from static information which is how it's usually done. It's "poor man's tags" but that's more than no tags at all. – Friedrich Jun 20 '23 at 09:15
  • 2
    Yes, `cmake --trace` could be used for collect all information requested in the question. For automatic processing of this information, it could be created in json format. See e.g. that [my answer](https://stackoverflow.com/a/76315014/3440745). – Tsyvarev Jun 20 '23 at 10:20
  • @Tsyvarev thank you for posting the link. I had your answer in the back of my head when I wrote my first comment. – Friedrich Jun 20 '23 at 12:19
  • Yes, I think `--trace` can be helpful. Using `cmake --trace-format=json-v1 --trace-redirect=trace.json <... other params>` followed by `jq '. | select(.cmd == "add_library" and (.args | index("module_a") // false))' trace.json` got me to the info I needed. I didn't want to bother learning `jq` but chatGPT generated the above so there might be a more elegant filter, but it works. – topher217 Jun 20 '23 at 17:39

1 Answers1

0

Universal Ctags recognizes a library CMakeLists.txt as a target:

$ find
.
./src
./src/main
./src/main/CMakeLists.txt
./src/module_a
./src/module_a/CMakeLists.txt
$ ctags --quiet --options=NONE -o - -R --fields=+nK
main    src/main/CMakeLists.txt /^add_executable(main$/;"   target  line:1
module_a    src/module_a/CMakeLists.txt /^add_library(module_a STATIC$/;"   target  line:1

An editor like Vim may provide a code navigation feature with a tags file. If readtags command that is part of Universal Ctags is available, you can create a tool for navigation based on the command:

$ ctags --quiet --options=NONE -o - -R --fields=+nK | readtags -t -  -F '(list "<" $name "> is defined at " $line " in " $input #t)' module_a
<module_a> is defined at 1 in src/module_a/CMakeLists.txt

(The pipeline works only if the code base is small enough; both ctags and readtags make temorary files.)

Masatake YAMATO
  • 1,210
  • 9
  • 10