74

The tool Include What You Use can be used to detect unneeded headers. I am using CMake for my C++ software project. How can I instruct CMake to run Include What You Use automatically on the source files of my software project?

Erik Sjölund
  • 10,690
  • 7
  • 46
  • 74
  • 30
    I needed to use include-what-you-use in my CMake project. When I found out this way to do it, I thought it was a good idea to document it as a stackoverflow question for others. Self-answers are encouraged according to the [documentation](http://stackoverflow.com/help/self-answer) – Erik Sjölund Jun 20 '15 at 15:28
  • 3
    There's even an "Answer your own question" checkbox when you ask a question. – Timmmm May 25 '17 at 10:23

4 Answers4

70

CMake 3.3 introduced the new target property CXX_INCLUDE_WHAT_YOU_USE that can be set to the path of the program include-what-you-use. For instance this CMakeLists.txt

cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
add_executable(hello main.cc)

find_program(iwyu_path NAMES include-what-you-use iwyu REQUIRED)

# If using CGAL<3.18, you remove REQUIRED and use
# if(NOT iwyu_path)
#   message(FATAL_ERROR "Could not find the program include-what-you-use")
# endif()

set_property(TARGET hello PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${iwyu_path})

is able to build the file main.cc

#include <iostream>
#include <vector>

int main() {
  std::cout << "Hello World!" << std::endl;
  return 0;
}

and at the same time have include-what-you-use give out a warning that the included header vector is not needed.

user@ubuntu:/tmp$ ls ~/hello
CMakeLists.txt  main.cc
user@ubuntu:/tmp$ mkdir /tmp/build
user@ubuntu:/tmp$ cd /tmp/build
user@ubuntu:/tmp/build$ ~/cmake-3.3.0-rc2-Linux-x86_64/bin/cmake ~/hello
-- The C compiler identification is GNU 4.9.2
-- The CXX compiler identification is GNU 4.9.2
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/build
user@ubuntu:/tmp/build$ make
Scanning dependencies of target hello
[ 50%] Building CXX object CMakeFiles/hello.dir/main.cc.o
Warning: include-what-you-use reported diagnostics:

/home/user/hello/main.cc should add these lines:

/home/user/hello/main.cc should remove these lines:
- #include <vector>  // lines 2-2

The full include-list for /home/user/hello/main.cc:
#include <iostream>  // for operator<<, basic_ostream, cout, endl, ostream
---

[100%] Linking CXX executable hello
[100%] Built target hello
user@ubuntu:/tmp/build$ ./hello
Hello World!
user@ubuntu:/tmp/build$

If you want to pass custom options to include-what-you-use, like for instance --mapping_file you can do it via

set(iwyu_path_and_options
    ${iwyu_path}
    -Xiwyu
    --mapping_file=${my_mapping})

set_property(TARGET hello
    PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${iwyu_path_and_options})
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Erik Sjölund
  • 10,690
  • 7
  • 46
  • 74
30

If you don't have access to CMake 3.3, include-what-you-use comes with a Python tool called iwyu_tool.py which can do what you want.

It works by parsing a JSON compilation database, which is easily produced with CMake (see below).

Running the tool manually

Assuming you already have a CMake build directory for your project, you first need to tell CMake to produce the compilation database:

cd build
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .

This generates a file, compile_commands.json containing compiler invocations for every object file in your project. You don't need to rebuild the project.

You can now run include-what-you-use on your project by running the Python tool on your build directory:

python /path/to/iwyu_tool.py -p .

Adding a custom target to your CMake project

The following snippet can be used to add an iwyu target to a CMake project.

# Generate clang compilation database
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

find_package(PythonInterp)
find_program(iwyu_tool_path NAMES iwyu_tool.py)
if (iwyu_tool_path AND PYTHONINTERP_FOUND)
  add_custom_target(iwyu
    ALL      # Remove ALL if you don't iwyu to be run by default.
    COMMAND "${PYTHON_EXECUTABLE}" "${iwyu_tool_path}" -p "${CMAKE_BINARY_DIR}"
    COMMENT "Running include-what-you-use tool"
    VERBATIM
  )
endif()

Notes

The include-what-you-use binary needs to be in your path for any of the above to work properly.

By default, iwyu_tool.py is single-threaded, which can be slow for large projects. You can use the --jobs argument to increase the number of source files that will be processed in parallel.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alastair Harrison
  • 1,281
  • 12
  • 14
  • i can't find iwyu_tool.py in the latest source. what would be the equivalent for that? – bysreg Nov 26 '16 at 16:49
  • @bysreg Are you talking about `include-what-you-use-0.7.src.tar.gz` downloaded from [http://include-what-you-use.org/downloads/](http://include-what-you-use.org/downloads/)? `iwyu_tool.py` appears to be still there, although it is deeply nested in the directory structure. Alternatively just download the script from the [github page](https://github.com/include-what-you-use/include-what-you-use). – Alastair Harrison Nov 27 '16 at 09:30
  • ah you're right. I was using clang 3.4 branch and it doesn't have that iwyu_tool.py. That file only exists from clang 3.6 upwards. Thanks for the help! – bysreg Nov 27 '16 at 18:39
  • Note that running the tool like this doesn't take advantage of any parallelism like you typically get with make -j. – Paul Brannan Mar 26 '17 at 16:35
  • It's called `iwyu_tool` and not `iwyu-tool`. You might want to correct your answer or others might run into a similar situation as I did when I searched for the latter and couldn't find that tool anywhere. – josch Aug 10 '17 at 18:28
  • Additionally, `find_program()` takes the `NAMES`, `HINTS`, and `PATHS` arguments which I've found helpful for gracefully handling both unix-y and Windows environments. – arclight Aug 10 '19 at 16:07
  • Does it need a clang compilation database only, or can it use gcc? – simplename Jun 08 '21 at 16:33
  • @simplename The format of the compilation database is independent of the compiler used. I've edited the text to reflect that. Thanks for pointing out the issue. – Alastair Harrison Jun 14 '21 at 10:09
12

You can also enable it globally outside the CMake script by setting the CMake variable:

cmake -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="iwyu" <builddir>

It will then call it on each CXX target.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user7159222
  • 121
  • 1
  • 2
4

I extended the source code from Alastair Harrison, in order to create a reusable solution. I've came up with the following that should work with all CMake versions:

File iwyu.cmake:

#.rst:
# include-what-you-use (iwyu)
# ----------------------------
#
# Allows to run the static code analyzer `include-what-you-use (iwyu)
# <http://include-what-you-use.org>`_ as a custom target with the build system
# `CMake <http://cmake.org>`_.
#
# .. topic:: Dependencies
#
#  This module requires the following *CMake* modules:
#
#  * ``FindPythonInterp``
#
# .. topic:: Contributors
#
#  * Florian Wolters <wolters.fl@gmail.com>

#===============================================================================
# Copyright 2015 Florian Wolters
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#===============================================================================

# ------------------------------------------------------------------------------
# Include guard for this file.
# ------------------------------------------------------------------------------

if(iwyu_included)
  return()
endif()

set(iwyu_included TRUE)

option(BUILD_IWYU
       "Run the include-what-you-use static analyzer on the source code of the project."
       OFF)

function(iwyu_enable)
  set(iwyu_EXECUTABLE_NAME include-what-you-use)
  find_program(iwyu_EXECUTABLE ${iwyu_EXECUTABLE_NAME})

  if(iwyu_EXECUTABLE)
    # This is not exactly the same behavior as with CMake v3.3, since here all
    # compiled targets are analyzed.
    set(iwyu_tool_EXECUTABLE_NAME iwyu_tool.py)

    find_package(PythonInterp)
    find_program(iwyu_tool_EXECUTABLE ${iwyu_tool_EXECUTABLE_NAME})

    if(PYTHONINTERP_FOUND AND iwyu_tool_EXECUTABLE)
      set(CMAKE_EXPORT_COMPILE_COMMANDS ON PARENT_SCOPE)

      add_custom_target(iwyu
                        ALL
                        COMMAND "${PYTHON_EXECUTABLE}" "${iwyu_tool_EXECUTABLE}" -p "${CMAKE_BINARY_DIR}"
                        COMMENT "Running the ${iwyu_tool_EXECUTABLE_NAME} compilation database driver"
                        VERBATIM)
    else()
      message(STATUS
              "Unable to find the Python interpreter and/or the ${iwyu_tool_EXECUTABLE_NAME} script")
    endif()
  else()
    message(STATUS "Unable to find the ${iwyu_EXECUTABLE_NAME} executable")
  endif()
endfunction()

File CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)

include(iwyu.cmake)

project(hello_world)
add_executable(${PROJECT_NAME} main.cc)

if(BUILD_IWYU)
  iwyu_enable()
endif()

Invoke CMake as follows to run include-what-you-use when the all target is invoked:

cmake -DBUILD_IWYU=ON <path-to-source>
cmake --build . --target all

The output should be as follows:

-- Configuring done
-- Generating done
-- Build files have been written to: /home/wolters/workspace/include-what-you-use_example/build
[ 66%] Built target hello_world
[100%] Running the iwyu_tool.py compilation database driver

/home/wolters/workspace/include-what-you-use_example/develop/main.cc should add these lines:

/home/wolters/workspace/include-what-you-use_example/develop/main.cc should remove these lines:
- #include <vector>  // lines 1-1

The full include-list for /home/wolters/workspace/include-what-you-use_example/develop/main.cc:
#include <iostream>  // for operator<<, basic_ostream, cout, endl, ostream
---
[100%] Built target iwyu
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Florian Wolters
  • 3,820
  • 5
  • 35
  • 55