Execute command or macro in CMake as the last step before the 'configure' step finishes
Asked Answered
A

3

11

Is it somehow possible with CMake (version >= 2.8.7) to execute a macro or command as a last step before the configuration phase finishes?

The functionality should be executed before the following lines get printed on screen:

-- Configuring done
-- Generating done

Up to now I wasn't able to find a CMake target which could be used as a dependency to achieve this with add_custom_command add_custom_target or add_dependencies.

EDIT: We have a library exporting several CMake macros and some of these macros must be executed at the end of each CMakeLists.txt file after all other CMake commands were run. Ideally the desired behavior can be achieved by including a file macros.cmake in a CMakeLists.txt file without the necessity to add an additional command at the end of this CMakeLists.txt file.

It would also be possible to achieve this by gathering all functionality in one macro which needs to be called explicitly at the end of the CMakeLists.txt. However, there are already several dependent libraries which would need to be adapted and a solution to this problem would omit this additional work. Also, adding the macro can be forgotten or the requirement for it being the very last statement can be easily violated.

Example macros.cmake:

macro(FINAL_MACRO)
    message(STATUS "Last step before finishing Configure phase")
endmacro()

# HERE: something like add_custom_target(final_steps)
# followed by something like add_dependencies(final_steps cmake_configure_finished)

Example toplevel CMakeLists.txt:

cmake_minimum_required(VERSION 2.8.7)
include(macros.cmake)

add_subdirectory(source)
add_subdirectory(interfaces)

# Here FINAL_MACRO should get executed without explicitly writing it down

If there is no other option we will have to require every user to call a special macro at the end of their CMakeLists.txt file.

Aggrieve answered 2/4, 2013 at 9:28 Comment(0)
V
7

If you're using CMake >=3.18, then you can use cmake_language(DEFER) to defer a call until the end of the current directory, or of another directory, e.g. CMAKE_SOURCE_DIR.

Example:

## Things to be done at the very end of configure phase
## as if they would be at bottom of CMakelists.txt
cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR} CALL _my_hook_end_of_configure())
function(_my_hook_end_of_configure)
    message("Hello end of configure!")
endfunction(_my_hook_end_of_configure)
Vassily answered 9/3, 2022 at 3:46 Comment(1)
This sounds like the only possibility officially supported. Thank you!Histrionism
I
6

OK - This answer feels a bit fragile since it's making use of undocumented CMake behaviour. However, it does seem to work.

Towards the end of the configuring process, after all commands in the CMakeLists.txt file have been processed, CMake checks the value of CMAKE_BACKWARDS_COMPATIBILITY. If this variable is being watched via the variable_watch command, then it will be triggered here.

So you can wrap this functionality into a couple of functions:

function(EOFHook Variable Access)
  if(${Variable} STREQUAL CMAKE_BACKWARDS_COMPATIBILITY AND
     (${Access} STREQUAL UNKNOWN_READ_ACCESS OR ${Access} STREQUAL READ_ACCESS))
    execute_process(COMMAND ${CMAKE_COMMAND} -E echo "In EOF hook command")
    ... do whatever else is required ...
  endif()
endfunction()

function(SetupEOFHook)
  variable_watch(CMAKE_BACKWARDS_COMPATIBILITY EOFHook)
endfunction()

To use this in any CMakeLists file, simply call SetupEOFHook() anywhere in the file.

It's a bit risky; if the variable is also read elsewhere in the CMakeLists.txt e.g. via

message(${CMAKE_BACKWARDS_COMPATIBILITY})

then it would trigger the EOFHook function here and at the end. You could add more complexity to the function by adding a counter and checking that it's only called once or else issue a message(FATAL_ERROR ...)

Irina answered 5/4, 2013 at 2:19 Comment(3)
Thank you very much for this interesting finding. This does achieve the desired behaviour. But I agree that it definitely feels a bit fragile. So maybe it is the best solution to require the explicit execution of a final function/macro until CMake offers an official way to do something like this.Aggrieve
@Aggrieve - Yes, if it were me, I'd definitely go for adding a single function/macro call at the end of every CMakeLists.txt. You could still try and enforce it's use by e.g. having the closing function unset a preprocessor definition which stops the build from working. A bit "hacky" too, but not as bad as depending on hidden CMake internal processes?Irina
CMAKE_PARENT_LIST_FILE and CMAKE_CURRENT_LIST_DIR seems to be more reliable, their value are set to empty at the end of the last file.Musquash
I
2

The CMake command execute_process will be executed during the configure phase rather than the build phase, so if you put this at the end of your CMakeLists.txt file, it will be executed last.

Irina answered 2/4, 2013 at 12:37 Comment(4)
Thanks for the answer. However, I am looking for something a little bit different. Unfortunately this wasn't stated in the question in the first place (see the edit).Aggrieve
Sorry - I still don't get it. It sounds like your asking to include(macros.cmake) part way through the main CMakeLists file, and have it execute a macro or function at the end of that CMakeLists file, but without explicitly calling the macro/function at the end of the file? Can you try and explain a bit more please, or even describe your use case?Irina
I've added another answer. I'll leave this one in place since it's probably the more "normal" way to achieve what is asked in the title.Irina
I upvoted this one, too. It is not the direct answer to the question but for now the correct way of doing it!Aggrieve

© 2022 - 2024 — McMap. All rights reserved.