Search code examples
cmakefunction-interposition

Override a CMake function twice


I have a CMake include file that overrides the built-in add_executable function, and I would like to figure out a way to do so transparently, so that if another file includes this file, but then also overrides add_executable, it will behave just as if they were interacting with the real add_executable. Here's a minimal CMakeLists.txt that demonstrates this problem.

cmake_minimum_required(VERSION 3.18)
project(interposition LANGUAGES C)

function(add_executable)
    message(STATUS "Inside of ${CMAKE_CURRENT_FUNCTION}")
    _add_executable(${ARGV})
endfunction()

function(add_executable)
    message(STATUS "Inside of the second copy of ${CMAKE_CURRENT_FUNCTION}")
    _add_executable(${ARGV})
endfunction()

add_executable(main main.c)

The idea of writing it this way is that each _add_executable symbol would refer to the previous definition of add_executable. In reality, this causes it to enter an infinite loop, where the first version of the function calls itself:

-- Inside of the second copy of add_executable
-- Inside of add_executable
-- Inside of add_executable
-- Inside of add_executable
...

If I change the first function to call __add_executable() (with two underscores), it says this:

-- Inside of the second copy of add_executable
-- Inside of add_executable
CMake Error at CMakeLists.txt:6 (__add_executable):
  Unknown CMake command "__add_executable".
Call Stack (most recent call first):
  CMakeLists.txt:11 (_add_executable)
  CMakeLists.txt:14 (add_executable)

If I rename the first function to _add_executable() and also call the two-underscore version, then it doesn't even get called:

-- Inside of the second copy of add_executable
-- Configuring done

Here's the one I most expected might work (change _add_executable() to cmake_language(CALL ...)

cmake_minimum_required(VERSION 3.18)
project(interposition LANGUAGES C)

function(add_executable)
    message(STATUS "Inside of ${CMAKE_CURRENT_FUNCTION}")
    cmake_language(CALL _${CMAKE_CURRENT_FUNCTION} ${ARGV})
endfunction()

function(add_executable)
    message(STATUS "Inside of the second copy of ${CMAKE_CURRENT_FUNCTION}")
    cmake_language(CALL _${CMAKE_CURRENT_FUNCTION} ${ARGV})
endfunction()

add_executable(main main.c)

In reality, this enters the same infinite loop as the original example.

The closest I've gotten to something that works is to rename the prefix the second function with a single underscore, and call the double-underscore version within it, but this means that the functions call each other in the wrong order, and throws out the window the idea of making this work transparently.

Is there any way to make this work so that

  1. The second copy of the function calls the first copy AND
  2. The second copy doesn't need to know of the existence of the first copy?

Solution

  • Thanks to Tsyvarev for pointing me in the right direction. His links led me to this thread, explaining that the feature is unsupported, and never intended to exist, as well as this article by Craig Scott, which confirms that multiple overrides will make the real function inaccessible.

    It's a very unsatisfying answer, but it's helpful to know that that's the official stance of the CMake developers.