CMake + precompiled headers + MSVC. Окончательный вариант

Как неожиданно выяснилось, предыдущие варианты (раз, два, три) оказались неправильными. Публикую окончательный правильный вариант использование precompiled headers при компиляции nmake или в Visual Studio 2008:

CMakeLists.txt

macro( use_precompiled_header SRC_LIST_VAR HDR_FILE SRC_FILE )
 
    get_filename_component( PCH_HEADER ${HDR_FILE} NAME )
    get_filename_component( PCH_BINARY ${HDR_FILE} NAME_WE )
 
    set( PCH_BINARY "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${PCH_BINARY}.pch" )
 
    if (MSVC)
        set( SRC_LIST ${${SRC_LIST_VAR}} )
 
        set_source_files_properties( ${SRC_LIST} PROPERTIES
            COMPILE_FLAGS "/Yu${PCH_HEADER} /Fp${PCH_BINARY}"
            OBJECT_DEPENDS "${PCH_BINARY}" )
 
        set_source_files_properties( ${SRC_FILE} PROPERTIES
            COMPILE_FLAGS "/Yc${PCH_HEADER} /Fp${PCH_BINARY}"
            OBJECT_OUTPUTS "${PCH_BINARY}"
            OBJECT_DEPENDS "" )
    endif(MSVC)
endmacro( use_precompiled_header )

Особенное внимание обратите на использование:

file( GLOB_RECURSE CORE_SRC *.cpp )
use_precompiled_header( CORE_SRC precompiled.h precompiled.cpp )

CORE_SRC — это НАЗВАНИЕ переменной, а не сама переменная.

P.S. С версии CMake 3.16.0 доступна команда target_precompiled_headers(), которая позволяет без лишних усилий работать с precompiled headers.

Используем OgreBullet в своем проекте с помощью CMake

В нашей системе сборки все сторонние библиотеки компилируются вместе с проектом, поэтому стандартное FIND_PACKAGE нам не подходит. Значит, CMakeLists.txt из сборки OgreBullet тоже идет лесом. Поэтому приходится извращаться на свой лад.

CMakeLists.txt

# Название проекта
project( OgreBullet )
 
# Разделяем исходники для двух precompiled headers
file( GLOB_RECURSE OGRE_BULLET_COLLISIONS Collisions/*.cpp )
file( GLOB_RECURSE OGRE_BULLET_DYNAMICS Dynamics/*.cpp )
 
# Папки для поиска #include
include_directories(
    ${GLOBAL_INCLUDE_DIR}/Ogre
    ${GLOBAL_INCLUDE_DIR}/OgreBullet/Collisions
    ${GLOBAL_INCLUDE_DIR}/OgreBullet/Dynamics
    ${GLOBAL_LIB_DIR}/Bullet
    ${GLOBAL_LIB_DIR}/Bullet/BulletConvexDecomposition
    ${GLOBAL_LIB_DIR}/Bullet )
 
# Определяем макрос для использования precompiled headers
macro(use_precompiled_header SRC_LIST HDR_FILE SRC_FILE)
    # Выделяем имя заголовка без пути
    get_filename_component(PCH_HEADER ${HDR_FILE} NAME)
    # Выделям имя заголовка без расширения и пути
    get_filename_component(PCH_BINARY ${HDR_FILE} NAME_WE)
    # Полный путь к созданному pch
    set(PCH_BINARY "${CMAKE_CURRENT_BINARY_DIR}/${PCH_BINARY}.pch")
 
    if (MSVC)
        # Для precompiled.cpp создаем pch, указывая файл (OBJECT_OUTPUTS), который создается в результате комманды 
        set_source_files_properties( ${SRC_FILE} PROPERTIES
            COMPILE_FLAGS "/Yc${PCH_HEADER} /Fp${PCH_BINARY}"
            OBJECT_OUTPUTS "${PCH_BINARY}" )
        # Для каждого исходника в списке, задаем зависимость от precompiled.pch и указываем использовать его параметром "/Yu"
        foreach( SOURCE ${SRC_LIST} )
            set_source_files_properties( ${SOURCE} PROPERTIES 
                COMPILE_FLAGS "/Yu${PCH_HEADER}" 
                OBJECT_DEPENDS "${PCH_BINARY}" )
        endforeach( SOURCE ${SRC_LIST} )
 
    endif(MSVC)
 
endmacro(use_precompiled_header)
 
# Создаем precompiled header для Collisions
use_precompiled_header( OGRE_BULLET_COLLISIONS
    "${GLOBAL_INCLUDE_DIR}/OgreBullet/Collisions/OgreBulletCollisions.h" Collisions/OgreBulletCollisionsPrecompiled.cpp )
 
# Создаем precompiled header для Dynamics
use_precompiled_header( OGRE_BULLET_DYNAMICS
    "${GLOBAL_INCLUDE_DIR}/OgreBullet/Dynamics/OgreBulletDynamics.h" Dynamics/OgreBulletDynamicsPrecompiled.cpp )
 
# Объединим два списка файлов для удобства
set( OGRE_BULLET_SRC
    "${OGRE_BULLET_COLLISIONS}"
    "${OGRE_BULLET_DYNAMICS}" )
 
# Добавляем define для использования precompiled headers библиотекой OgreBullet
add_definitions( -D_PRECOMP )
# Profit!
add_library( ogre_bullet STATIC ${OGRE_BULLET_SRC} )

UPDATE: Окончательный вариант precompiled headers смотрим здесь.

Использование precompiled headers в CMake – 2

Еще один один вариант использования precompiled headers в CMake. Подсмотрено в Ogre3D:

#-------------------------------------------------------------------
# This file is part of the CMake build system for OGRE
#     (Object-oriented Graphics Rendering Engine)
# For the latest info, see http://www.ogre3d.org/
#
# The contents of this file are placed in the public domain. Feel
# free to make use of it in any way you like.
#-------------------------------------------------------------------
 
##################################################################
# Support macro to use a precompiled header
# Usage:
#   use_precompiled_header(TARGET HEADER_FILE SRC_FILE)
##################################################################
 
macro(use_precompiled_header TARGET HEADER_FILE SRC_FILE)
  get_filename_component(HEADER ${HEADER_FILE} NAME)
 
  if (MSVC)
    add_definitions(/Yu"${HEADER}")
    set_source_files_properties(${SRC_FILE}
      PROPERTIES COMPILE_FLAGS /Yc"${HEADER}"
    )
 
  elseif (CMAKE_COMPILER_IS_GNUCXX)
    # disabled because it seems to increase compile time
    ## this is some serious hack... we definitely need native 
    ## support in CMake for this!
    ## we will generate the precompiled header via a workaround
    ## first give the header a new name with the proper extension
    #set(PRECOMP_HEADER ${CMAKE_CURRENT_BINARY_DIR}/hacked/${HEADER}.gch)
    #configure_file(${HEADER_FILE} ${PRECOMP_HEADER} COPYONLY)
    ## retrieve some info about the target's build settings
    #get_target_property(${TARGET} PRECOMP_TYPE TYPE)
    #if (PRECOMP_TYPE STREQUAL "SHARED_LIBRARY")
    #  set(PRECOMP_LIBTYPE "SHARED")
    #else ()
    #  set(PRECOMP_LIBTYPE "STATIC")
    #endif ()
    #get_target_property(${TARGET} PRECOMP_DEFINITIONS COMPILE_DEFINITIONS)
    #get_target_property(${TARGET} PRECOMP_FLAGS COMPILE_FLAGS)
    #
    ## add a new target which compiles the header
    #add_library(__precomp_header ${PRECOMP_LIBTYPE} ${PRECOMP_HEADER})
    #add_dependencies(${TARGET} __precomp_header)
    #set_target_properties(__precomp_header PROPERTIES
    #  COMPILE_DEFINITIONS ${PRECOMP_DEFINITIONS}
    #  COMPILE_FLAGS ${PRECOMP_FLAGS}
    #  HAS_CXX TRUE
    #)
    #set_source_files_properties(${PRECOMP_HEADER} PROPERTIES
    #  HEADER_FILE_ONLY FALSE
    #  KEEP_EXTENSION TRUE
    #  COMPILE_FLAGS "-x c++-header"
    #  LANGUAGE CXX
    #)
    #
    ## finally, we need to ensure that gcc can find the precompiled header
    ## this is another dirty hack
    #include_directories(BEFORE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/__precomp_header.dir/hacked")
 
  endif ()
endmacro(use_precompiled_header)

Использование также тривиально:

FILE( GLOB_RECURSE SRC *.cpp )
USE_PRECOMPILED_HEADER( projectName "precompiled.h" "precompiled.cpp" )

UPDATE: Окончательный вариант precompiled headers смотрим здесь.

Использование precompiled headers в CMake

Макрос для использования precompiled headers в CMake + Visual Studio 2008.

macro(USE_PRECOMPILED_HEADER PrecompiledHeader Sources)
    get_filename_component(HEADER_NAME ${PrecompiledHeader} NAME_WE)
    set(PrecompiledBinary ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${HEADER_NAME}.pch)
    set(PrecompiledSource ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${HEADER_NAME}.cpp)
    set(NEED_UPDATE_HEADER_CPP TRUE)
    set(PrecompiledData "#include \"${PrecompiledHeader}\"")
    if (EXISTS ${PrecompiledSource})
        file(READ ${PrecompiledSource} HEADER_CPP_DATA_IN)
        if (NOT ${HEADER_CPP_DATA_IN} STREQUAL ${PrecompiledData})
            SET(NEED_UPDATE_HEADER_CPP TRUE)
        else ()
            SET(NEED_UPDATE_HEADER_CPP FALSE)
        endif ()
    endif ()
    if (NEED_UPDATE_HEADER_CPP)
        file(WRITE ${PrecompiledSource} ${PrecompiledData})
    endif ()
    set_source_files_properties(${PrecompiledSource}
        PROPERTIES
        COMPILE_FLAGS "/Yc${PrecompiledHeader} /Fp${PrecompiledBinary}"
        OBJECT_OUTPUTS ${PrecompiledBinary}
    )
    set(DUPLICATE_SOURCES ${${Sources}})
    set_source_files_properties(${DUPLICATE_SOURCES}
        PROPERTIES
        COMPILE_FLAGS "/Yu${PrecompiledBinary} /FI${PrecompiledBinary} /Fp${PrecompiledBinary}"
        OBJECT_DEPENDS ${PrecompiledBinary}
    )
    source_group(src ${PrecompiledSource})
    list(APPEND ${Sources} ${PrecompiledSource})
endmacro(USE_PRECOMPILED_HEADER)

Использование тривиально:

FILE( GLOB_RECURSE SRC *.cpp )
USE_PRECOMPILED_HEADER("precompiled.h" SRC )

UPDATE: Окончательный вариант precompiled headers смотрим здесь.

Пустая переменная CMAKE_BUILD_TYPE при запуске INSTALL из MSVC

Для генерация файлов сборки проекта в последнее время пользуюсь замечательной утилитой CMake.

В процессе использования утилиты наткнулся на неприятный баг. Переменная CMAKE_BUILD_TYPE при сборки из под Visual Studio 2008 всегда оказывается пустой, вне зависимости от выбранного типа сборки (Debug, Release, RelWithDebInfo).

Решение проблемы заключается в использовании макроса CMAKE_INSTALL_CONFIG_NAME. Макрос этот хитрый и в документации к CMake вы его не найдете. Он используется только в сгенерированном файле cmake_install.cmake при выполнении цели INSTALL.

Пример использования этого макроса

set( INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/bin/\${CMAKE_INSTALL_CONFIG_NAME})

Обратите внимание на обратный слеш после «bin/», он не дает раскрыть макрос до выполнения скрипта cmake_install.cmake, который вызывается из Visual Studio 2008 при компиляции цели INSTALL

Блог Евгения Жирнова