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.

Some key techniques:

  • UPDATE_DISCONNECTED: this helps avoid CMake constantly rebuilding ExternalProject
  • PATCH_COMMAND: This has a previous configure_file that generates a correct Makefile.inc for the computer + compiler being used
  • CONFIGURE_COMMAND: 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: build the first target(s) that are required by targets in subsequent steps. If there’s no subsequent targets, this is the only build step.
  • INSTALL_COMMAND: We “install” into the project’s build/ hierarchy, as otherwise some external projects have very deep directory structures are are annoying to deal with. Very long paths also cause breakage on Windows.
  • 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
  • ExternalProject_Add_Step: If additional targets need to be built (what in the plain project would be multiple make commands), use Steps to do this in order.
ExternalProject_Add(Scotch
GIT_REPOSITORY https://gitlab.inria.fr/scotch/scotch.git
GIT_TAG v6.1.0
UPDATE_DISCONNECTED true
PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/Makefile.inc ${_src}
CONFIGURE_COMMAND ""
BUILD_COMMAND ${MAKE_EXECUTABLE} -j${Ncpu} -C ${_src} ${_targ}
INSTALL_COMMAND ${MAKE_EXECUTABLE} -j${Ncpu} -C ${_src} install prefix=${PROJECT_BINARY_DIR}
BUILD_BYPRODUCTS ${Scotch_LIBRARIES}  # Ninja needs this
)

ExternalProject_Add_Step(Scotch build_esmumps
COMMAND ${MAKE_EXECUTABLE} -j${Ncpu} -C ${_src}/esmumps install
DEPENDEES build
DEPENDERS install
)