[CMake] Boost's CMAKE approach and the BUILD_INTERFACE generator expression

paul pfultz2 at yahoo.com
Fri Oct 27 19:16:07 EDT 2017


On Fri, 2017-10-27 at 12:22 -0700, Wesley Smith wrote:
> Boost's CMAKE page (http://bcm.readthedocs.io/en/latest/src/Building.html> says:
> 
> So this will build the library named boost_filesystem, however, we need to
> supply the dependencies to boost_filesystem and add the include directories.
> To add the include directory we use target_include_directories. For this, we
> tell cmake to use local include directory, but since this is only valid
> during build and not after installation, we use the BUILD_INTERFACE
> generator expression so that cmake will only use it during build and not
> installation:
> 
> target_include_directories(boost_filesystem PUBLIC
>     $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
> )
> 
> 
> Is is necessary to use a BUILD_INTERFACE here?  Couldn't you use
> PUBLIC/PRIVATE/INTERFACE to achieve the same effect?  What are the use cases
> over for BUILD_INTERFACE that setting include dirs as
> PUBLIC/PRIVATE/INTERFACE doesn't cover?

You don't need `BUILD_INTERFACE` if it is set to `PRIVATE`, as none of the
downstream users will use the include. However, when using `PUBLIC` or
`INTERFACE` you will need `BUILD_INTERFACE`, and in the example above it using
`PUBLIC`.

This is because there are two types of consumers using the target. One is a
target within the build. This will use the include directory from the
source(or build) directory. The `BUILD_INTERFACE` ensures that this is only
used for this type of consumer.

The other type of consumer is the what is used after installation. In this
case the include directory is different and most likely points to the
directory in installation directory like `${CMAKE_INSTALL_PREFIX}/include`.
The `INSTALL_INTERFACE` ensures that this is only used for this type of
consumer.

So for this case, you setup the includes something like this:

target_include_directories(boost_filesystem PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
)

This will ensure that each type of consumer get the correct include directory.
Ultimately, you don't want the user to add an include to a local source
directory, as this could have surprising side effect. Fortunately, cmake
ensures that this won't happen either, by producing an error if the
installations include paths that point to directories in the source or build
directory.

At the same token, you also don't want the build to point to the installation
include as well as this may include headers you don't want to use.

Finally, in the example above it didn't use the `INSTALL_PREFIX`. This is
because the install sets it correctly when using the `INCLUDES DESTINATION`
statement:

install(TARGETS boost_filesystem EXPORT boost_filesystem-targets
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
    INCLUDES DESTINATION include
)

Paul


More information about the CMake mailing list