Search code examples
cmakebuild-dependencies

Create CMake target only if dependencies satisified


I have a CMake project with multiple libraries and executables. Some targets have dependencies that might not be satisfied on all platforms. In this case, I'd like CMake to work and generate the Makefiles (or other build projects) that make sense.

For example:

  • libFOO -> depends libX11 (say)
  • execBAR -> depends libFOO
  • exexQUUX -> no deps

On a system with libX11 installed, all targets can be built. On a system without, only execQUUX will be available to be built.

At the moment I am doing something like this:

# work out if we can build this target
set(buildLibFOO LibX11_FOUND)

if (${buildLibFOO})
    add_library(libFOO ... )
    target_link_libraries(libFOO LibX11_LIBRARY)
endif()

set(buildExecBAR true)

# work out if we can build this target
if (NOT TARGET libFOO)
    set(buildExecBAR false)
endif()

if (${buildExecBAR})
    add_exec(execBAR ...)
    target_link_libraries(execBAR libFOO)
endif() 

# always build this one
add_exec(execQUUX ...)

However, this feels a bit kludgy, as I need to think carefully about what to check for for each target, rather than saying to CMake "these deps are required for these targets, if missing, omit the target".

Is there a simpler (or more correct) way to do this?


Solution

  • these deps are required for these targets, if missing, omit the target

    As far as I know, there is no such functionality in CMake.

    But you can create function, which adds library/executable only when its dependencies are satisfied. Something like this:

    include(CMakeParseArguments)
    
    # Similar as *add_executable*, but additionally accepts DEPENDS option.
    # All arguments after this option are treated as libraries targets for link with;
    # executable is created only when these targets exist.
    function(add_executable_depends name)
        cmake_parse_arguments(MY_EXEC "" "" "DEPENDS" ${ARGN})
        foreach(depend ${MY_EXEC_DEPENDS})
            if(NOT TARGET ${depend})
                return()
            endif()
        endforeach()
        add_executable(${name} ${MY_EXEC_UNPARSED_ARGUMENTS})
        target_link_libraries(${name} ${MY_EXEC_DEPENDS})
    endfunction()
    

    Example of usage:

    # work out if we can build this target
    set(buildLibFOO LibX11_FOUND)
    
    if (${buildLibFOO})
        add_library(libFOO ... )
        target_link_libraries(libFOO LibX11_LIBRARY)
    endif()
    
    # Add executable (and link with libFOO) only when libFOO exists.
    add_executable_depends(execBAR ... DEPENDS libFOO)
    
    # always build this one(similar to add_executable)
    add_executable_depends(execQUUX ...)