Search code examples
boostcmakeboost-buildb2

Build Boost library at CMake configure time


Some time ago I was looking for a solution on SO how to automatically build BOOST library at CMake configure time. I didn't find a complete solution, but I did find useful part about FetchContent. I wrote a CMake script to do this. But this script created a complete library. Then I extended this script to use the "bcp" tool from BOOST to create only the necessary libraries.

So, How to automatically build the necessary BOOST libraries at CMake configure time?


Solution

  • Here is my solution to build and install only the circular_buffer library (with dependencies) from BOOST:

    cmake_minimum_required(VERSION 3.20)
    
    # set common paths
    set(EXTERNAL_PROJECTS_DIR   "${CMAKE_SOURCE_DIR}/.external")
    set(EXEC_INSTALL_DIR        "${CMAKE_INSTALL_PREFIX}/EXEC")
    
    # set paths
    set(BOOST_LIB_ROOT_DIR      "${EXTERNAL_PROJECTS_DIR}/boost")
    set(BOOST_LIB_REPOS_DIR     "${BOOST_LIB_ROOT_DIR}/repos")
    set(BOOST_LIB_REPOS_MIN_DIR "${BOOST_LIB_ROOT_DIR}/repos-minimal")
    set(BOOST_LIB_BUILD_DIR     "${BOOST_LIB_ROOT_DIR}/build")
    set(BOOST_LIB_FCSTUFF_DIR   "${BOOST_LIB_ROOT_DIR}/cmake-fetchcontent-stuff")
    cmake_path(GET CMAKE_BINARY_DIR FILENAME CONFIG_NAME)
    if (CONFIG_NAME)
      set(BOOST_LIB_INSTALL_DIR "${BOOST_LIB_ROOT_DIR}-install/${CONFIG_NAME}")
    else()
      set(BOOST_LIB_INSTALL_DIR "${BOOST_LIB_ROOT_DIR}-install")
    endif()
    
    set(BOOST_LIB_INCLUDE_DIR   "${BOOST_LIB_INSTALL_DIR}/include")
    set(BOOST_LIB_LIB_DIR       "${BOOST_LIB_INSTALL_DIR}/lib")
    
    # set script options and commands for the build
    set(BOOST_LIB_BUILD_OPTIONS         "--includedir=${BOOST_LIB_INCLUDE_DIR}")
    list(APPEND BOOST_LIB_BUILD_OPTIONS "--libdir=${BOOST_LIB_LIB_DIR}")
    list(APPEND BOOST_LIB_BUILD_OPTIONS "--build-dir=${BOOST_LIB_BUILD_DIR}")
    list(APPEND BOOST_LIB_BUILD_OPTIONS "--build-type=minimal")
    list(APPEND BOOST_LIB_BUILD_OPTIONS "--no-cmake-config")
    
    set(BOOST_LIB_BUILD_PROPERTIES            "address-model=64")
    list(APPEND BOOST_LIB_BUILD_PROPERTIES    "link=static")
    if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
      list(APPEND BOOST_LIB_BUILD_PROPERTIES  "variant=debug")
    else()
      list(APPEND BOOST_LIB_BUILD_PROPERTIES  "variant=release")
    endif()
    
    # set bcp module-list
    # vvv required modules vvv
    set(BCP_MODULE_LIST         "build")
    list(APPEND BCP_MODULE_LIST "bootstrap.bat")
    list(APPEND BCP_MODULE_LIST "bootstrap.sh")
    list(APPEND BCP_MODULE_LIST "boostcpp.jam")
    list(APPEND BCP_MODULE_LIST "boost-build.jam")
    list(APPEND BCP_MODULE_LIST "config")
    # ^^^ required modules ^^^
    # vvv optional modules vvv
    list(APPEND BCP_MODULE_LIST "circular_buffer")
    # ^^^ optional modules ^^^
    
    # download and unpack BOOST library
    include(FetchContent)
    FetchContent_Declare(
      boost
      GIT_REPOSITORY  https://github.com/boostorg/boost.git
      GIT_TAG         "boost-1.81.0"
      PREFIX          ${BOOST_LIB_FCSTUFF_DIR}
      SOURCE_DIR      ${BOOST_LIB_REPOS_DIR}
    )
    set(FETCHCONTENT_QUIET OFF CACHE BOOL "" FORCE)
    
    FetchContent_GetProperties(boost)
    if(NOT boost_POPULATED)
      FetchContent_Populate(boost)
    endif()
    
    # run build and installation
    if(${boost_POPULATED})
      if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
        file(GLOB_RECURSE VCVARSALL_LIST "C:/Program Files*/vcvarsall.bat")
        list(POP_BACK VCVARSALL_LIST VCVARSALL_FILE)
      endif()
    
      file(GLOB B2_EXE_PATH "${BOOST_LIB_REPOS_DIR}/b2*")
      list(LENGTH B2_EXE_PATH B2_EXE_PATH_LENGTH)
      if (B2_EXE_PATH_LENGTH EQUAL 0)
        if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
          set(EXECUTE_PROCESS_COMMAND ${VCVARSALL_FILE} "x64" "&&" ".\\bootstrap.bat" "msvc")
        elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU AND WIN32)
          set(EXECUTE_PROCESS_COMMAND ".\\bootstrap.bat" "gcc")
        elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU AND LINUX)
          set(EXECUTE_PROCESS_COMMAND "./bootstrap.sh" "gcc")
        endif()
        execute_process(
          COMMAND           ${EXECUTE_PROCESS_COMMAND}
          WORKING_DIRECTORY ${BOOST_LIB_REPOS_DIR}
          COMMAND_ECHO      STDOUT
        )
      endif()
    
    
      file(GLOB BCP_EXE_PATH "${BOOST_LIB_REPOS_DIR}/dist/bin/bcp*")
      list(LENGTH BCP_EXE_PATH BCP_EXE_PATH_LENGTH)
      if (BCP_EXE_PATH_LENGTH EQUAL 0)
        if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
          set(EXECUTE_PROCESS_COMMAND ${VCVARSALL_FILE} "x64" "&&" ".\\b2" "tools/bcp" "toolset=msvc")
        elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU AND WIN32)
          set(EXECUTE_PROCESS_COMMAND ".\\b2" "tools/bcp" "toolset=gcc")
        elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU AND LINUX)
          set(EXECUTE_PROCESS_COMMAND "./b2" "tools/bcp" "toolset=gcc")
        endif()
        execute_process(
          COMMAND           ${EXECUTE_PROCESS_COMMAND}
          WORKING_DIRECTORY ${BOOST_LIB_REPOS_DIR}
          COMMAND_ECHO      STDOUT
        )
      endif()
    
    
      # check bcp module-list for changes
      set(BCP_MODULE_LIST_HASH_FILENAME "bcp-module-list-hash")
      string(SHA256 BCP_MODULE_LIST_HASH_NEW "${BCP_MODULE_LIST}")
      file(MAKE_DIRECTORY ${BOOST_LIB_REPOS_MIN_DIR})
      file(TOUCH "${BOOST_LIB_REPOS_MIN_DIR}/${BCP_MODULE_LIST_HASH_FILENAME}")
      file(READ "${BOOST_LIB_REPOS_MIN_DIR}/${BCP_MODULE_LIST_HASH_FILENAME}" BCP_MODULE_LIST_HASH_OLD)
      string(COMPARE EQUAL "${BCP_MODULE_LIST_HASH_NEW}" "${BCP_MODULE_LIST_HASH_OLD}" BCP_MODULE_LIST_HASHES_ARE_EQUAL)
      if (NOT ${BCP_MODULE_LIST_HASHES_ARE_EQUAL})
        file(REMOVE_RECURSE ${BOOST_LIB_REPOS_MIN_DIR})
        file(MAKE_DIRECTORY ${BOOST_LIB_REPOS_MIN_DIR})
        file(WRITE "${BOOST_LIB_REPOS_MIN_DIR}/${BCP_MODULE_LIST_HASH_FILENAME}" "${BCP_MODULE_LIST_HASH_NEW}")
        if(WIN32)
          set(EXECUTE_PROCESS_COMMAND ".\\bcp" "--boost=${BOOST_LIB_REPOS_DIR}" ${BCP_MODULE_LIST} "${BOOST_LIB_REPOS_MIN_DIR}")
        elseif(LINUX)
          set(EXECUTE_PROCESS_COMMAND "./bcp" "--boost=${BOOST_LIB_REPOS_DIR}" ${BCP_MODULE_LIST} "${BOOST_LIB_REPOS_MIN_DIR}")
        endif()
        execute_process(
          COMMAND           ${EXECUTE_PROCESS_COMMAND}
          WORKING_DIRECTORY "${BOOST_LIB_REPOS_DIR}/dist/bin"
          COMMAND_ECHO      STDOUT
        )
      endif()
    
    
      file(MAKE_DIRECTORY ${BOOST_LIB_INSTALL_DIR})
      file(TOUCH "${BOOST_LIB_INSTALL_DIR}/${BCP_MODULE_LIST_HASH_FILENAME}")
      file(READ "${BOOST_LIB_INSTALL_DIR}/${BCP_MODULE_LIST_HASH_FILENAME}" BCP_MODULE_LIST_HASH_OLD)
      string(COMPARE EQUAL "${BCP_MODULE_LIST_HASH_NEW}" "${BCP_MODULE_LIST_HASH_OLD}" BCP_MODULE_LIST_HASHES_ARE_EQUAL)
      if(NOT ${BCP_MODULE_LIST_HASHES_ARE_EQUAL})
        file(REMOVE_RECURSE ${BOOST_LIB_INSTALL_DIR})
        file(MAKE_DIRECTORY ${BOOST_LIB_INSTALL_DIR})
        file(WRITE "${BOOST_LIB_INSTALL_DIR}/${BCP_MODULE_LIST_HASH_FILENAME}" "${BCP_MODULE_LIST_HASH_NEW}")
        if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
          set(EXECUTE_PROCESS_COMMAND_1 ${VCVARSALL_FILE} "x64" "&&" "bootstrap.bat" "msvc")
          set(EXECUTE_PROCESS_COMMAND_2 ${VCVARSALL_FILE} "x64" "&&" "bootstrap.bat" "msvc")
          set(EXECUTE_PROCESS_COMMAND_3 ${VCVARSALL_FILE} "x64" "&&" ".\\b2" "toolset=msvc" ${BOOST_LIB_BUILD_OPTIONS} ${BOOST_LIB_BUILD_PROPERTIES} "install")
        elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU AND WIN32)
          set(EXECUTE_PROCESS_COMMAND_1 ".\\bootstrap.bat" "gcc")
          set(EXECUTE_PROCESS_COMMAND_2 ".\\bootstrap.bat" "gcc")
          set(EXECUTE_PROCESS_COMMAND_3 ".\\b2" "toolset=gcc" ${BOOST_LIB_BUILD_OPTIONS} ${BOOST_LIB_BUILD_PROPERTIES} "install")
        elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU AND LINUX)
          set(EXECUTE_PROCESS_COMMAND_1 "./bootstrap.sh" "gcc")
          set(EXECUTE_PROCESS_COMMAND_2 "./bootstrap.sh" "gcc")
          set(EXECUTE_PROCESS_COMMAND_3 "./b2" "toolset=gcc" ${BOOST_LIB_BUILD_OPTIONS} ${BOOST_LIB_BUILD_PROPERTIES} "install")
        endif()
        execute_process(
          COMMAND           ${EXECUTE_PROCESS_COMMAND_1}
          WORKING_DIRECTORY "${BOOST_LIB_REPOS_MIN_DIR}/tools/build"
          COMMAND_ECHO      STDOUT
        )
        execute_process(
          COMMAND           ${EXECUTE_PROCESS_COMMAND_2}
          WORKING_DIRECTORY ${BOOST_LIB_REPOS_MIN_DIR}
          COMMAND_ECHO      STDOUT
        )
        execute_process(
          COMMAND           ${EXECUTE_PROCESS_COMMAND_3}
          WORKING_DIRECTORY ${BOOST_LIB_REPOS_MIN_DIR}
          COMMAND_ECHO      STDOUT
        )
      endif()
    endif()
    
    set(BOOST_INCLUDES "${BOOST_LIB_INCLUDE_DIR}")
    if(WIN32)
      string(APPEND BOOST_INCLUDES "/boost-1_81")
    endif()
    file(GLOB_RECURSE BOOST_LIBS "${BOOST_LIB_LIB_DIR}")
    

    Steps taken:

    1. Set variables
    2. Set "bcp" module-list
    3. Download BOOST (using FetchContent)
    4. Build "b2" (Boost.Build)
    5. Build "bcp" tool
    6. Install the library sources from the "bcp" module-list in a separate directory
    7. Build and install these libraries

    Here is the full version: https://github.com/weenchvd/cmake-build-boost-lib