Mikko Koivunalho
Mikko Koivunalho

Reputation: 341

How to "hook" into Cmake command add_executable or add_library to run something extra?

We build with CMake and normal Unix makefiles. There are some static analysis checks, e.g. Cppcheck, we run at every C/C++ file in the project to catch errors at compile time.

I have created a custom target for cppcheck and attached it into "all" target. This checks all the *.c and *.cpp files in the project.

We want to run a check every time a file is changed and recompiled and only on that file. The check should be run automatically and without the user having to add extra commands in CMake. Essentially, the check should be "attached/hooked" to normal CMake commands add_library() and add_executable(). Is there any way to do this in CMake?

Upvotes: 5

Views: 2688

Answers (3)

Andry
Andry

Reputation: 2727

You can try hookup functionality from the tacklelib library: https://github.com/andry81/tacklelib/tree/HEAD/cmake/tacklelib/Handlers.cmake

I don't guarantee it will work for a system function, but you can try by following the examples from the tests: https://github.com/andry81/tacklelib/tree/HEAD/cmake_tests/unit/01_script_mode/11_Handlers/

Some example from the tests:

include(tacklelib/Handlers)
include(tacklelib/Props)

# handler we want to attach
macro(custom_pre_handler)
  tkl_test_assert_true("a STREQUAL \"111\"" "1 call context variables is not visible: a=${a}")

  tkl_append_global_prop(. call_sequence -1)
endmacro()

# macro function where the handler should be attached
macro(custom_macro)
  tkl_test_assert_true("a STREQUAL \"111\"" "2 call context variables is not visible: a=${a}")

  tkl_append_global_prop(. call_sequence 0)
endmacro()

# registering context for target to attach
tkl_enable_handlers(PRE_POST function custom_macro)

# attaching, PRE - before the call
tkl_add_last_handler(PRE custom_macro custom_pre_handler)

# testing attachment, custom_pre_handler must be called before custom_macro
set(a 111)

custom_macro()

# the last state
tkl_get_global_prop(call_sequence call_sequence 0)

# the call order test
tkl_test_assert_true("call_sequence STREQUAL \"-1;0\"" "call sequence is invalid: call_sequence=${call_sequence}")

# a cmake test internally wrapped into a function
return()

tkl_test_assert_true(0 "unreachable code")

Upvotes: 0

d-karl
d-karl

Reputation: 98

Assuming you have a list of source files (which you should).

Iterate over the source files with a for_each loop. For each source file, use add_custom_command that will run the cppcheck tool on the file. Make that custom_command DEPEND on the file in the current loop. You should now have custom commands for all individual source files, that will trigger if, and only if the files change because of the DEPENDS instruction.

Not that these commands will have to creat some sort of output file. I suggest piping the output of cppcheck into a file named $source$_test.

Documentation: https://cmake.org/cmake/help/latest/command/add_custom_command.html

Upvotes: 2

Tsyvarev
Tsyvarev

Reputation: 65898

While add_executable (and add_library) is provided by CMake itself, you may define a function or a macro with the same name, which would "hide" the original CMake function. Inside your function/macro you may call original CMake function using underscore-prefixed name:

function(add_executable target_name)
   # Call the original function
   _add_executable(${target_name} ${ARGN})
   ... perform additional steps...
endfunction(add_executable target_name)

Upvotes: 6

Related Questions