6

I am trying to add a custom target with CMake that executes one command for each given .cpp file. The command should only be re-executed when the source file itself or one of the included source files changes. AFAIK to achieve this I need a list of all the included files and add them to the DEPENDS option of the add_custom_command() calls that belong to my custom target.

So is there a built-in way to get that list of included files?

I know about the IMPLICIT_DEPENDS option of the add_custom_command() function but it only works for Makefile generators. I would like to make this work for all generators.

Thank you for your time

Edit:

As requested I will post some cmake code to show what I want to achieve. I want to add a custom target, that runs clang-tidy on all the given .cpp files. When incrementally building the custom target the clang-tidy commands should be re-run whenever a .cpp file or one of its directly or indirectly included header files is changed. Just like re-runs of the compiler are handled.

# ----------------------------------------------------------------------------------------
# mainTargetName The name of the target that shall be analyzed
# files A list of all the main targets .cpp files
#
function( addStaticAnalysisTarget mainTargetName files )

    set(targetName runStaticAnalysis_${mainTargetName})
    set(command "clang-tidy-4.0 -checks=* -p ${CMAKE_BINARY_DIR}")

    foreach( file ${files}  )

        get_filename_component( baseName ${file} NAME_WE)
        set(stampFile ${CMAKE_CURRENT_BINARY_DIR}/analyze_${baseName}.stamp )
        set(fullFile ${CMAKE_CURRENT_SOURCE_DIR}/${file})
        set(commandWithFile "${command} ${fullFile}")
        separate_arguments_for_platform( commandList ${commandWithFile})

        add_custom_command(
            OUTPUT ${stampFile}
            DEPENDS "${fullFile}"
            IMPLICIT_DEPENDS CXX "${fullFile}"
            COMMAND ${commandList}
            COMMAND cmake -E touch "${stampFile}"       # without creating a file as a touch-stone the command will always be re-run.
            WORKING_DIRECTORY ${CPPCODEBASE_ROOT_DIR}
            COMMENT "${commandWithFile}"
            VERBATIM
        )

        list(APPEND stampFiles ${stampFile})

    endforeach()
    set_source_files_properties(${stampFiles} PROPERTIES GENERATED TRUE)   # make the stamp files known to cmake as generated files.

    add_custom_target(
        ${targetName}
        DEPENDS ${stampFiles}
    )

endfunction()

The problem with that is, that it does not seem to work. When I change included files clang-tidy is not re-run for the affected files. I used the "Unix Makefile" generator for this example so it should work at least with make. Any hints why it doesn't?

My hopes where that I could achieve the desired behavior for all generators by somehow getting the file-dependencies at cmake time and then adding them to the ''''DEPENDS'''' list. But the dependency scanning must be done each time the command is run, so it can not be done at cmake time. This means that the scanning must be implemented by cmake which it currently is not.

A guy with similar problems: https://gitlab.kitware.com/cmake/cmake/issues/16830

Edit 2: I think the problem that the IMPLICIT_DEPENDS option was not working was because I did not use correct filenames. I changed that in the code snipped, but I have not yet tested if it works in the project.

Knitschi
  • 2,822
  • 3
  • 32
  • 51
  • So you want CMake to somehow find what files your source file itself includes? – tambre Apr 21 '17 at 17:15
  • Just for clarification: Do you mean `cmake -E cmake_depends` calls with "CMake's cpp file dependency-scanner"? – Florian Apr 21 '17 at 19:40
  • @tambre Exactly – Knitschi Apr 26 '17 at 09:57
  • @Florian: Yes but preferably not by executing a command line and parsing the result but by using a cmake-function in the CMakeLists file. – Knitschi Apr 26 '17 at 09:58
  • @Knitschi CMake is not a compiler. It can't or won't be able to know that. If you want that, then you'll need to call an outside program to do that for you. – tambre Apr 27 '17 at 04:56
  • @Florian: Do you know if the cmake -E cmake_depends command still works? I tried to use it, but it is an unoffical command and there is no documentation of it. – Knitschi Apr 27 '17 at 06:20
  • @tambre: So you say that this is not possible with cmake? For now I will then try the IMPLICIT_DEPENDS option and have my custom target only when using the make toolchains. The thing is that I could have sworn I read something about a file dependency scanner in the docs, but now I can not find the page. Maybe It was just a whishfull dream :-) – Knitschi Apr 27 '17 at 06:35
  • 1
    You can find the "documentation" [here](https://github.com/Kitware/CMake/blob/aaeb01ef9bfa600e072be79792be53186c395a8c/Source/cmcmd.cxx#L772). And yes, it is still used by CMake internally. But I've never had the need to directly call it, so can you please give a [mcve] of what you are actually trying to do? There may be other ways. – Florian Apr 27 '17 at 07:54
  • What is your targeted "non makefile" build environment? And did you know about [`CXX_CLANG_TIDY`](https://cmake.org/cmake/help/latest/prop_tgt/LANG_CLANG_TIDY.html) target property? See e.g. [here](http://stackoverflow.com/questions/40433573/how-can-i-specify-additional-arguments-for-use-with-cmake-cxx-clang-tidy-variabl) or [here](https://www.reddit.com/r/cpp/comments/5b397d/what_c_linter_do_you_use/) – Florian May 10 '17 at 19:32
  • For clang-tidy it is only the makefile environment for now. When I wrote the question I was hoping to solve the problem in a general way, but I gave up on that and I will live with what I have. The ````CXX_CLANG_TIDY```` option sounds interesting, but for now I think I will stick to what I have because I use many checks and I don't want to slow down my builds. – Knitschi May 15 '17 at 11:00
  • If I look at the `clang-tidy` [command line options](http://clang.llvm.org/extra/clang-tidy/) `-p` does require [`CMAKE_EXPORT_COMPILE_COMMANDS`](https://cmake.org/cmake/help/latest/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html) which also only works for makefile environments. So I doubt that running/using `clang-tidy` out-of CMake without makefiles or Ninja would work. So is it an option to use a separate build output directory for the static code analysis? Separating source code build and static code analysis would simplify the problem. – Florian Jun 27 '17 at 19:33
  • Do you happen to know if the ````IMPLICIT_DEPENDS```` option works for ninja? That would solve the problem on windows if clang-tidy should become available there. – Knitschi Jun 28 '17 at 05:44

1 Answers1

1

I think the answer to my question is ...

No, you can not use cmakes dependency scanner in the cmake code.

That makes sense, because this problem can not be solved at cmake time, because the dependencies of a .cpp file may change without cmake being re-run. The problem must be solved within cmake itself at make time. This is done when using the IMPLICIT_DEPENDS option.

Also, I tried to solve a Problem that I did not really have, because at this point I can only run clang-tidy on linux anyways. However, clang-tidy may become available on windows as well and then I may have the problem again.

To sum the comments up:

  • Tambre stated that CMake is not a compiler and therefore can not do that. I think this is wrong. According to this article, CMake can parse cpp include dependencies because make has no such dependency searcher itself. That was news to me, but I mostly live on Windows so I am not that familiar with make. It could also be possible that in the meantime make was extended to do its own dependency searching. Also this explains why the IMPLICIT_DEPENDS option is only available for make.

  • Florian pointed out that it is not necessary to create an own custom target for running clang-tidy. Instead, one can use the CXX_CLANG_TIDY target property to run clang-tidy for each file after compiling it. This means however, that static-analysis can not be separated from the build which could lead to inacceptable buildtimes.

  • There is the cmake -E cmake_depends command line, that could be used to retrieve dependencies at cmake time. But as stated above, I erroneously thought that I needed the dependencies at cmake time, while I needed them at runtime.

  • The IMPLICIT_DEPENDS options did not work because I had an error in my cmake code.

Knitschi
  • 2,822
  • 3
  • 32
  • 51