CMake expects a real compiler when it runs its initial checks, so setting IWYU as the compiler directly doesn't work.
Following this mail is a script I cooked up a while back for just this scenario. It invokes the compiler as usual and IWYU in addition when it makes sense.
See the header comment for usage details (... and don't forget to give execute permissions).
--Zaheer
---- BEGIN: iwyu-wrapper -------------------------------------------------------
#!/usr/bin/env bash
# Compiler wrapper script for include-what-you-use (IWYU).
#
# This is required for build systems (e.g. CMake) which perform configuration
# checks and thus require a functioning compiler. First, the compiler is
# invoked as usual and then IWYU is called in addition. Note this will increase
# build times since each file is parsed twice.
#
# This script should be called as a prefix to the build command, for instance:
# $ iwyu-wrapper clang -c hello.c -o hello.o
# To use this from CMake, configure with a command such as:
# $ cmake -DCMAKE_C_COMPILER="iwyu-wrapper clang" \
# -DCMAKE_CXX_COMPILER="iwyu-wrapper clang++" ..
#
# Build command output is not redirected, however IWYU reports are placed into
# files with the extension '.iwyu.log'. An example of how to conveniently fix
# the includes using the given advice is as follows:
# $ find . -name '*.iwyu.log' -exec cat {} \; > __all__.iwyu.log
# $ python fix_includes.py --comments --nosafe_headers < __all__.iwyu.log
set -e # Die if any command fails.
if [[ $# -lt 1 ]] ; then
echo "Usage: $0 <build command>"
exit 1
fi
# Execute the build command itself.
# "$@" returns all positional parameters whilst preserving quoting.
"$@"
# IWYU expects exactly one compiler job so we check for commands of the form:
# $ clang -c hello.c -o hello.o
# The aim of this is to ignore commands which don't involve compilation, e.g.:
# $ clang a.o b.o c.o -o xyz
# otherwise these would unnecessarily result in a build failure.
compile_only=false
output_file=
for ((i = 2; i <= $#; i++)); do
# ${!i} returns the i-th positional command-line argument.
case "${!i}" in
-c)
compile_only=true
;;
-o)
# Output file is given by next argument.
(( i++ ))
output_file=${!i}
;;
esac
done
if [[ -z "${output_file}" ]]; then
echo "Error: build command did not specify an output file (-o)"
exit 1
fi
# Finally execute IWYU.
if ${compile_only} ; then
# IWYU seems to return exit code 1 even on success, so we need to be more
# permissive and temporarily toggle the failure mode.
set +e
# "${@:i}" returns all parameters from position i onwards (preserves quotes).
include-what-you-use "${@:2}" > "${output_file}.iwyu.log" 2>&1
if [[ $rc != 1 ]] ; then
exit $rc
fi
set -e
fi
---- END: iwyu-wrapper ---------------------------------------------------------