User defined hooks for CMake "events" (design discussion)
I maintain a CMake framework (smth like KDE's ECM) which is a basic dependency for around a hundred C++ projects in the company. It simplifies "end-user" CMakeLists.txt
in many aspects.
One of the things I'm thinking about (wishing) for a long time is user-defined hooks for CMake events. Events like the following:
- entering/leaving directory by
add_subdrectory()
- adding/modifying a target
- entering/leaving a project scope
Examples,
-
on entering a unit tests directory (
*/tests
) it's very common to havepaths.cpp.in
(having definitions of some filesystem paths required by tests here) which must be rendered withconfigure_file()
-
on adding an executable target which name suffixed with
*_unit_tests
themain.cpp
file must be rendered and added to the sources. As well as the${CMAKE_CURRENT_BINARY_DIR}/paths.cpp
if exists - we have quite formal rules for grouping sources of targets and it'll be nice to apply
source_group()
for every target defined on leaving a directory - for every adding target we have the rule to make an alias depending on the target type
- for every adding target we have a bunch of properties to assign (including
INTERFACE_*
andCOMPATIBLE_INTERFACE_*
) that indicates vital build features - for every project added we have a well know list of variables to set (sometimes depending on project properties) like top project options (so all our projects have the same set of them), rendering CMake configs for
find_package()
, common CPack related stuff... - ... etcetera
The framework provides a bunch of functions an "end-user" have to call at specific points of arbitrary CMakeLists.txt
in the project's subdirectory. This approach has a quite regular set of functions to call per sub-directory but at the same time is error-prone.
Trying to simplify it even more as the last resort I could write a super_function_that_do_all_the_job_in_any_subdirectory()
that do magic^W all things that have to be done in a single call. Having very formal rules it's quite possible... However, this function gonna have zillion parameters weakly related to each other and for sure gonna look not very nice.
That is why I'm thinking about how nice it gonna be to have smth like this:
function(my_entering_subdirectory DIRNAME)
if(DIRNAME MATCHES ".*/tests$")
# Render `*.in` files`
...
endif()
endfunction()
list(APPEND CMAKE_USER_HOOKS_ENTERING_SUBDIRECTORY my_entering_subdirectory)
function(my_leaving_subdirectory DIRNAME)
...
endfunction()
list(APPEND CMAKE_USER_HOOKS_LEAVING_SUBDIRECTORY my_leaving_subdirectory)
function(my_adding_target TARGET)
...
endfunction()
list(APPEND CMAKE_USER_HOOKS_ADDING_TARGET my_adding_target)
function(my_modifying_target TARGET [WHAT_HAS_CHANGED ...])
...
endfunction()
list(APPEND CMAKE_USER_HOOKS_MODIFYING_TARGET my_modifying_target)
...
add_subdirectory(blah-blah)
So, any CMakeLists.txt
in the subdirectory gonna have a "clean" CMake instructions: add_library
or add_executable
plus target_blah_blah()
commands. And all that magic gonna happened in the user defined hooks (OK, provided by the framework %).
Some of that magic could be done via watching (variable_watch()
) CMake specific variables but this way is not increasing configuration speed for sure and personally I consider it as abuse. I would be happy to have a "legal" (straightforward) way to do that...
Thoughts?