I have a large-ish codebase which builds several dozen libaries and several executables.
The codebase is broken down hierarchically and libraries are build at pretty muc
If the libraries are truly independent projects then it makes sense to use the project
command. However if they are not I would instead just add them as subdirectories in your root CmakeLists.txt
. You can use the variables CMAKE_CURRENT_SOURCE_DIR and CMAKE_CURRENT_BINARY_DIR if you need to know the directories that are currently being processed.
I found a good usage example of exactly this today: adding Doxygen documentation.
I use CMake (and Ninja) to build my personal C++ projects. I decided on a whim to add some Doxygen documentation to one of my pretty much complete but undocumented efforts. I also thought it would be neat to add it to the other projects as well just as soon as I work out how to make it as generic as possible.
To begin with, I generated a standard Doxygen template and renamed it.
cd my_projects/projectx
doxygen -g Doxyfile
mv Doxyfile Doxyfile.in
Note the .in extension. Probably not necessary but conventional, if I understand correctly.
Next, I added the following code block in my CMakeLists.txt file, just before defining my targets (not sure if that's important but CMake is sometimes fussy about the sequence of certain commands).
FIND_PACKAGE(Doxygen)
IF("${DOXYGEN_FOUND}" MATCHES "^YES$")
CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
@ONLY)
ADD_CUSTOM_TARGET( doc ALL
COMMAND ${DOXYGEN_EXECUTABLE}
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Doxygenating..."
VERBATIM)
ENDIF()
This creates a new target called doc. Specifying ALL adds it to the default "all" target, but that's optional. Specifying @ONLY ensures that any "${variable}" type variables will not be expanded by CONFIGURE_FILE, only "@variable@" types. Somewhat confusingly (to me at least), CMAKE_CURRENT_SOURCE_DIR appears to refer to the project directory and CMAKE_CURRENT_BINARY_DIR to the build directory.
Finally, and this is where PROJECT_NAME et al come in, I edited Doxyfile.in.
This is the beginning of my new Doxyfile.in:
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "@PROJECT_NAME@"
PROJECT_NUMBER = @PROJECT_VERSION_TWEAK@
PROJECT_BRIEF =
PROJECT_LOGO = @CMAKE_CURRENT_SOURCE_DIR@/res/doc_logo-200x55.png
OUTPUT_DIRECTORY = @CMAKE_CURRENT_SOURCE_DIR@/doc
You get the idea, I think. Once this is fully genericised (is that a word?) I can copy it to my other projects and as long as I tag up my code, I will have nice documentation everywhere.
Notice PROJECT_BRIEF isn't specified. I haven't finished with this and there still a few blanks for me to think about. For instance PROJECT_VERSION_TWEAK doesn't actually contain anything yet. I will have to find a way to get my build number in there.
Firstly, it enables you to use <projectName>_BINARY_DIR
and <projectName>_SOURCE_DIR
, but that's not the main advantage. If you give CMake a project name then it will generate build targets for each of the sub-projects in their own directories. This means that whether you're using GNU Make, Eclipse CDT, XCode, or any of the other supported generators you can build sub-projects individually. For instance with GNU Make each sub-project has its own full build system from it's own directory.
You can access the current project name through PROJECT_NAME
, and the root project name by CMAKE_PROJECT_NAME
.
Edit: I've just realised the below will be standard CMake behaviour for any of its build targets whether they're projects or not. I'll keep it here for general information but it is not pertinent to the answer:
Assume I have a C++ library, and I can generate three binary executables; Main
and tests/test1
, and examples/ex1
. I can either run make in the directory I called CMake from with the ALL target, run make ex1
, or I can change directory to examples/
and build the examples with make
from that directory. This will build all of the dependent projects and libraries even if they're somewhere else in the directory structure but won't build Main
or tests/test1
or any libraries that they depend on that examples/ex1
doesn't. If I then run make from the main directory, it won't rebuild any of the libraries that examples/ex1
depends on unless their source has changed.