[CMake] Adding pre-compiled header support easily (add_generated_source)

Oliver Smith osmith at playnet.com
Wed Feb 17 22:47:49 EST 2010


Adding precompiled header support doesn't have to be a massive amount of 
work.

Fundamentals:

    1. Include the header in every source module,
    2. Compile the header once for every target (so that compiler flags 
match),

There are three possible build environments:

    1. Full precompiled header support
    2. Precompiled header support for a non-PCH aware source tree,
    3. No precompiled header support,

Lets deal with #1 first; there will be a precompiled header file that 
needs compiling ahead of the build target. MSVC and ICC will detect 
incompatible PCHs and warn the user, GCC 3.4+ will do that if you 
specify -Winvalid-pch.

Other than that, it's a simple matter of adding defining the mechanisms 
per-compiler for building the PCH and telling the compiler to use it.

For MSVC/ICC "/Yu'${${PROJECT}_PCH_NAME}'".

For GCC it's a little fiddlier, because it has to be either in the same 
folder as the header file or before it in the search path. Adding -I 
${CMAKE_BINARY_DIR} is sub-optimal because now every #include searches 
an extra directory.

Scenario 2 can be dealt with through the use of MSVC/ICCs /FI (force 
include) and gcc's -include option.

For scenario #3 there are two options. a/ For every source file, create 
a sourcename.pch.${SOURCE_SUFFIX} file, which contains

#include <${${PROJECT}_PCH_NAME}>
#include "${${PROJECT}$_SOURCE_DIR}/${SOURCE_FILE}"

CMake could do this fairly simply by using something like CONFIGURE_FILE.

The other would be to define a macro that the user could put in every 
source file that #includes the PCH, and not actually pre-compile the 
header file.

(If the compiler supports forced inclusion, you could use that, but I'm 
not aware of any compiler that supports forced includes without also 
supporting PCH).

Implementing a "one size fits all" PCH solution is obviously not trivial 
work, and I understand the "roll your own" stance being taken. But CMake 
could move us a long way closer to making that practical /and/ solve 
some other problems in a single, fairly easy to implement step that also 
tackles issues with wrapper systems like tolua/swig/ecpg (postgres' 
embedded sql in c):

    add_generated_source(
	<filepath>
	TARGET <ALL | list of targets>
	SOURCE <filepath>
	[COMPILE_FLAGS [APPEND] <additional compile flags>]
	[EXCLUDE_FLAGS] <list of compile flags to exclude>
	[CUSTOM_COMMAND <filepath> <command line arguments>]
	[DEPENDS ...]
	[WORKING_DIRECTORY <filepath>]
	[PRE_BUILD | PRE_LINK]
    )

Variables defined inside:
    ${SOURCE} The source file name (to reduce the need for duplication)
    ${SOURCE_NO_SUFFIX} The source file name minus the final suffix
    ${SOURCE_SUFFIX} The suffix of the source file
    ${OUTPUT} The name of the output file (allowing filepath to be 
"something/${SOURCE_NO_SUFFIX}.${TARGET}.${SOURCE_SUFFIX}")
    ${OUTPUT_NO_SUFFIX}
    ${OUTPUT_SUFFIX}
    ${TARGET} The name of the target I'm being applied to

This doesn't eliminate the "roll your own" PCH solution entirely, but it 
gives a much clearer way of describing how to do it for the majority of 
cases (people trying to cross-support the "Big 4" of MSVC/ICC/XCode/GNU).

Example usage with the "precompiled header" part marked bold:

    # Call user-defined macro to find tolua++, figure out which database to use, etc.
    find_packages()

    # Add pre-compiled header support
    IF ( MSVC )

    *	add_generated_source(
    		${Project_SOURCE_DIR}/includes/stdafx.h
    		TARGET ALL
    		SOURCE ${Project_SOURCE_DIR}/includes/stdafx.h
    		COMPILE_FLAGS "/Yc${SOURCE}"
    		EXCLUDE_FLAGS "/Yu${SOURCE}"
    		PRE_BUILD
    	)
    *
    ELSE IF ( COMPILER_IS_GNU )

    *	 add_generated_source(
    		${Project_SOURCE_DIR}/includes/stadafx.gch
    		TARGET ALL
    		SOURCE ${Project_SOURCE_DIR}/includes/stdafx.h
    		COMPILE_FLAGS "-o ${OUTPUT}"
    		EXCLUDE_FLAGS "-include ${SOURCE}"
    		PRE_BUILD
    	)*

    ELSE ( COMPILER_IS_GNU )

    	MESSAGE(FATAL_ERROR "Only works for MSVC/GNU")

    ENDIF ( MSVC OR COMPILER_IS_GNU )

    # Build a per-target lua wrapper.
    # i.e. build "src/luaWrapper.XXX.cc" from "etc/luaWrapper.XXX.pkg".
    # Always gets generated and then compiled just before the link step.
    add_generated_source(
    	${Project_SOURCE_DIR}/src/luaWrapper.${TARGET}.cc
    	TARGET ALL
    	SOURCE ${Project_SOURCE_DIR}/etc/${SOURCE_NO_SUFFIX}.${TARGET}.pkg
    	CUSTOM_COMMAND ${TOULAPP_EXECUTABLE} -n ${TARGET} -o ${OUTPUT} ${SOURCE}
    	*PRE_LINK
    *)


    # Library automatically gets src/luaWrapper.common.cc added to its compilation,
    # Also automatically builds and then includes the precompiled header with PROJ_LIBRARY defined.
    add_library(common
    	${Project_SOURCE_DIR}/src/common.cpp
    	${Project_SOURCE_DIR}/src/lua.cpp
    )
    set_property(TARGET common PROPERTY COMPILE_DEFINITIONS PROJ_LIBRARY)


    # Client automatically gets src/luaWrapper.client.cc added and the precompiled header
    # built with PROJ_CLIENT defined instead of PROJ_LIBRARY
    add_target(client
    	${Project_SOURCE_DIR}/src/clientMain.cpp
    	${Project_SOURCE_DIR}/src/clientEngine.cpp
    	${Project_SOURCE_DIR}/src/common-depends.cpp
    )
    set_property(TARGET client PROPERTY COMPILE_DEFINITIONS PROJ_CLIENT)
    target_link_libraries(common)


    # Server automatically gets src/luaWrapper.server.cc added and the precompiled header
    # built with PROJ_SERVER defined
    add_target(server
    	${Project_SOURCE_DIR}/src/serverMain.cpp
    	${Project_SOURCE_DIR}/src/serverEngine.cpp
    	${Project_SOURCE_DIR}/src/common-depends.cpp
    	${Project_SOURCE_DIR}/src/serverDatabase.cpp
    )
    set_property(TARGET server PROPERTY COMPILE_DEFINITIONS PROJ_SERVER PROJ_DATABASE=${PROJ_DATABASE})
    target_link_libraries(common ${DATABASE_LIBRARIES})
      


- Oliver
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.cmake.org/pipermail/cmake/attachments/20100217/57616024/attachment-0001.htm>


More information about the CMake mailing list