With add_custom_target, users can execute custom commands within targets. This is subtly different from the add_custom_command recipe we have discussed previously. The target added by add_custom_target has no output and is thus always executed. It is thus possible to introduce a custom target in subdirectories, and still be able to refer to it in the top-level CMakeLists.txt.
In this example, we have extracted an archive of source files using a combination of add_custom_target and add_custom_command. These source files were later used to compile a library that we managed to link against in a different (parent) directory scope. In the construction of the CMakeLists.txt files, we briefly commented that the tarball is extracted under deps, one subdirectory below the build directory of the project. This is because in CMake, the structure of the build tree mimics the hierarchy of the source tree.
A remarkable detail in this recipe, which we should discuss, is the curious fact that we have marked the math library sources as PRIVATE:
set(MATH_SRCS
${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxBLAS.cpp
${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxLAPACK.cpp
${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxBLAS.hpp
${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxLAPACK.hpp
)
# ...
add_library(math "")
target_sources(math
PRIVATE
${MATH_SRCS}
)
# ...
Although these sources are PRIVATE, we compiled linear-algebra.cpp in the parent scope and this source code includes CxxBLAS.hpp and CxxLAPACK.hpp. Why is PRIVATE used here, and how was it possible to compile linear-algebra.cpp and build the executable? Had we marked the header files as PUBLIC, CMake would have stopped at CMake time with an error, "Cannot find source file", since the to-be-generated (extracted) source files do not exist in the file tree yet.
This is a known limitation (see https://gitlab.kitware.com/cmake/cmake/issues/14633, as well as a related blog post: https://samthursfield.wordpress.com/2015/11/21/cmake-dependencies-between-targets-and-files-and-custom-commands). We have worked around this limitation by declaring the sources PRIVATE. By doing this, we did not get any file dependencies on non-existent sources at CMake time. However, the CMake built-in C/C++ file dependency scanner picked them up at build time and the sources compiled and linked.