[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