Build Make external projects from CMake

CMake ExternalProject allows building a wide variety of subprojects isolated from the main CMake project. For GNU Make Makefile projects, it is necessary to invoke the make command. However, there are several programs named “make” across operating systems. To help ensure the correct GNU Make is selected, we do:

find_program(MAKE_EXECUTABLE
NAMES gmake mingw32-make make
NAMES_PER_DIR
DOC "GNU Make")

A real-life example of CMake with Makefile ExternalProject has multiple Make invocations to build separate Make target groups, where later Make targets depend on the other Make targets being built first. We just show a snippet here for clarity, omitting definition of some of the obvious variables used.

UPDATE_DISCONNECTED true
helps avoid CMake constantly rebuilding ExternalProject.
CONFIGURE_COMMAND ""
since Make doesn’t have a configure step, so we must define this blank, as otherwise CMake will try to find a CMakeLists.txt in the external project code.
BUILD_COMMAND
builds the first target(s) that are required by targets in subsequent steps. If there’s no subsequent targets, this is the only build step.
BUILD_BYPRODUCTS
In general we point this at the “installed” files, as otherwise Ninja will error on cmake --build build. Ninja is stricter than Make about the target to source graph
include(GNUInstallDirs)

find_program(MAKE_EXECUTABLE NAMES gmake make mingw32-make REQUIRED)

set(my_LIBRARY ${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}my${CMAKE_STATIC_LIBRARY_SUFFIX})

ExternalProject_Add(my
GIT_REPOSITORY https://github.com/username/my.git
GIT_TAG main
UPDATE_DISCONNECTED true
CONFIGURE_COMMAND ""
BUILD_COMMAND ${MAKE_EXECUTABLE} -j -C <SOURCE_DIR>
INSTALL_COMMAND ${MAKE_EXECUTABLE} -j -C <SOURCE_DIR> install prefix=${CMAKE_INSTALL_PREFIX}
BUILD_BYPRODUCTS ${my_LIBRARY}
)