muxelplexer - $/>

CMake Tips: Managing dependency warnings

Posted on 2024.07.15

I belong to the tribe of people who think that CMake is a pretty good tool. I do find myself needing to struggle due to the sheer amount of parallel API’s. One of these struggles i’ve gone through lately was when i finally got to setup clang-tidy.

Having static-analyzation is great - it catches many semantic issues before the build is done and keeps your from some regressions. I’m also a big fan of using -Werror and -pedantic to keep myself from checking in incorrect or failing commits.

This is where CMake comes back to play: Over the last couple years two modules have swept the scene and support most to all needs you will have with your dependencies: FetchContent and ExternalProject.

Both include your dependencies in a way similar to add_subdirectory and thus declare them as techhnically your code. This can get extremely annoying with aforementioned settings as you are not at the hands of the original developers to not break your rule-chains.

This can be avoided though!

CMake uses a nifty little feature called SYSTEM_INCLUDE_DIRECTORIES that are being handled slightly different to the normal kind of include directories we know. CMake assumes these are provided by the system - thus not really of it’s concern in terms of formatting/static-analyzing. We as developers can tell CMake to add a target’s include directories to this list so that we can keep ignoring those upstream warnings.

FetchContent

If you use FetchContent it’s actually extremely easy. All you have to do is add SYSTEM to the Fetchconent_Declare statement. For example the popular fmt package would be declared like this:

FetchContent_Declare(fmt
  SYSTEM
  GIT_REPOSITORY https://github.com/fmtlib/fmt.git
  GIT_SHALLOW ON
  GIT_TAG master
)
FetchContent_MakeAvailable(fmt)

ExternalProject

Here it gets a little harder, since ExternalProject offers quite a verbose and customizable API we need to first ExternalProject_Add our project, then retrieve the include-directories as setup by ExternalProject and overwrite the target’s properties. This can be done like this: (using libjpeg-turbo as example)

ExternalProject_Add(extern-libjpeg
    GIT_REPOSITORY "https://github.com/libjpeg-turbo/libjpeg-turbo"
    GIT_TAG "3.0.3"
    GIT_SHALLOW ON
    PREFIX ${LIBJPEG_TURBO_PREFIX}
    CMAKE_ARGS ${LIBJPEG_TURBO_CMAKE_ARGS}
    BUILD_BYYPRODUCTS ${LIBJPEG_TURBO_INSTALL_PREFIX}/lib/libjpeg.a
)
add_library(jpeg STATIC IMPORTED GLOBAL)
add_dependencies(jpeg extern-libjpeg)

set(LIBJPEG_TURBO_INCLUDE_DIRS ${LIBJPEG_TURBO_INSTALL_PREFIX}/include)
file(MAKE_DIRECTORY ${LIBJPEG_TURBO_INCLUDE_DIRS})

set_target_properties(jpeg PROPERTIES
    IMPORTED_LOCATION ${LIBJPEG_TURBO_INSTALL_PREFIX}/lib/libjpeg.a
    INTERFACE_INCLUDE_DIRECTORIES ${LIBJPEG_TURBO_INCLUDE_DIRS}
    INTERFACE_SYSTEM_INCLUDE_DIRECTORIES ${LIBJPEG_TURBO_INCLUDE_DIRS}
)

The call to file is needed since the include directory needs to exist at configuration time, so we pre-create it.