Build autotools as CMake ExternalProject

Building an autotools project as a CMake ExternalProject saves the time of converting that other project to CMake. This technique makes it easy to automatically build that other project when it’s not easily installable otherwise, or you wish to build it optimized. This technique has project-specific aspects, but here is a high-level overview. One of the key issues this technique overcomes is the ExternalProject rebuilding each time the main CMake project is rebuilt.

This example is simplified from the p4est-cmake project.

Main CMakeLists.txt

if(NOT p4est_external)
  find_package(p4est)
endif()

if(p4est_external OR NOT p4est_FOUND)
  include(cmake/p4est.cmake)
endif()

cmake/p4est.cmake

set(p4est_external true CACHE BOOL "build p4est library")

include(ExternalProject)

set(p4est_LIBRARIES)
# list each of the library binaries used by the project
foreach(_l p4est sc)
  list(APPEND p4est_LIBRARIES ${PROJECT_BINARY_DIR}/lib/lib${_l}${CMAKE_STATIC_LIBRARY_SUFFIX})
endforeach()

if(EXISTS ${PROJECT_BINARY_DIR}/lib/libp4est${CMAKE_STATIC_LIBRARY_SUFFIX})
  # this is what we use to tell that the ExternalProject was already built,
  # and to then hide the ExternalProject_Add
  set(p4est_FOUND true)
endif()

if(NOT p4est_FOUND)

  ExternalProject_Add(p4est_proj
  GIT_REPOSITORY https://github.com/cburstedde/p4est.git
  GIT_TAG prev3-develop
  PREFIX ${PROJECT_BINARY_DIR}
  CONFIGURE_COMMAND ${PROJECT_BINARY_DIR}/src/p4est_proj/configure
  BUILD_COMMAND make -j
  INSTALL_COMMAND make -j install
  TEST_COMMAND ""
  BUILD_BYPRODUCTS ${PROJECT_BINARY_DIR}/src/p4est_proj-build/src/.libs/libp4est.${CMAKE_STATIC_LIBRARY_SUFFIX}
  )

  ExternalProject_Get_Property(p4est_proj SOURCE_DIR)

  ExternalProject_Add_Step(p4est_proj
    bootstrap
    COMMAND ./bootstrap
    DEPENDEES download
    DEPENDERS configure
    WORKING_DIRECTORY ${SOURCE_DIR})

  file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/include)  # avoid race condition
endif()

add_library(p4est::p4est INTERFACE IMPORTED GLOBAL)
target_include_directories(p4est::p4est INTERFACE ${PROJECT_BINARY_DIR}/include)
target_link_libraries(p4est::p4est INTERFACE ${p4est_LIBRARIES})
# set_target_properties didn't work, but target_link_libraries did work

if(NOT p4est_FOUND)
  # this is necessary to trigger the ExternalProject
  add_dependencies(p4est::p4est p4est_proj)
endif()