5

When repeatedly building a project, when there are warnings but no errors in a translation unit, the main source file is typically not recompiled.

This can make it difficult to work through errors and warnings to attempt to get the project to build with no warnings. Typically one must keep iteratively building until all errors are taken care of, then do a full clean and build to ensure that there are no warnings (as well as to ensure that the previously-completed build wasn't a "fluke" caused by leftover build artifacts).

Is there any way with CMake (or some other utility such as a Bash script) to parse build output for warnings, save them in a text file somewhere, and then re-display them on subsequent builds?

For bonus points, since I'm colorizing my compiler output, can warnings be saved with the color control-characters and re-displayed with the same colorization?

(If it matters, at the moment I'm only compiling C++, and I'm typically using GCC to do so. My build generator of choice is Ninja, and I have some Bash scripts I've written that wrap all my calls to CMake and Ninja.)

Kyle Strand
  • 15,941
  • 8
  • 72
  • 167
  • Why do not compile with `-Werror`, when all warnings are treated as errors, which prevents source file's compilation? It is easy to parse build output for warnings, but how you expect them being redisplaying, when even output may changes(no output for already compiled files at all)? – Tsyvarev Jul 10 '15 at 22:03
  • @Tsyvarev That's a good idea, but it has some flaws. The most basic is that warnings simply aren't as important as errors, and in general, if target `B` depends on target `A`, and target `A` has a warning but target `B` has an actual error, I want to see the error in `B` as quickly as possible rather than causing the build to die because of a non-fatal warning. – Kyle Strand Jul 10 '15 at 22:08
  • Ideally, I'd like to go through my iterative re-compiling process for taking care of errors without even worrying about (most) warnings, and ultimately end up with a list of warnings seen along the way, which I could then go back and examine. (Obviously warnings are sometimes helpful for figuring out the root cause of compile errors, but that's not the case when the only diagnostics in a translation unit are warnings, which is the case I'm finding problematic.) – Kyle Strand Jul 10 '15 at 22:09
  • Another way: store warnings from every iterative compilation, then join them with removing duplicates. As result you will have warnings *as if* collected from clean build. – Tsyvarev Jul 10 '15 at 22:46
  • @Tsyvarev That's exactly what I'm asking how to do. – Kyle Strand Jul 10 '15 at 22:48
  • 2
    There are 10 difficult problems in computer science: Cache invalidation, naming things and off-by-one errors. You would hit the first one hard. – steveire Jul 11 '15 at 00:18
  • @Steveire Yep, this is non-trivial. – Kyle Strand Jul 11 '15 at 03:58
  • Just two general hints: One way in CMake to do something with `stderr` output is to use the [RULE_LAUNCH_COMPILE](http://www.cmake.org/cmake/help/v3.3/prop_dir/RULE_LAUNCH_COMPILE.html) (see e.g. [here](http://stackoverflow.com/questions/21592167/cmake-can-i-filter-compiler-output-through-another-program) or [here](http://stackoverflow.com/questions/14399984/make-cmake-use-gccfilter)). And tools like [ccache](https://ccache.samba.org/) claim to "providing exactly the same compiler warnings that would be produced if you use the real compiler". So combining both I think a solution is possible. – Florian Jul 13 '15 at 07:23

1 Answers1

5

I'm not a bash expert - so the following code can certainly be improved - but here is a working example with CMake/bash/gcc/ninja that should give the basic idea I had:

  • Detect if the compiler puts warnings/errors on stderr
  • Store it in <object file name>.warnings
  • Delete the object file that had a warning before the next build is started
  • The compiler itself will output the warning again (if not already fixed)

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(CaptureWarnings CXX)

add_compile_options(-fdiagnostics-color=always -Wconversion)
configure_file(capture_warnings.sh.in ${CMAKE_BINARY_DIR}/capture_warnings.sh @ONLY)

file(WRITE foo.cc "int main() {\nreturn 0.5;\n}")
file(WRITE bar.cc "")

set_directory_properties(PROPERTIES 
    RULE_LAUNCH_COMPILE "bash ${CMAKE_BINARY_DIR}/capture_warnings.sh")

add_executable(MyExe foo.cc bar.cc)

capture_warnings.sh.in

#!/bin/bash

# shell script invoked with the following arguments
# $(CXX) $(CXX_DEFINES) $(CXX_FLAGS) -MMD -MT OBJ_FILE -MF DEP_FILE -o OBJ_FILE -c SRC_FILE

# extract parameters
OBJECT_FILE="${@: -3:1}"

# invoke compiler
set -o pipefail
$@ 2> ${OBJECT_FILE}.warnings
ERROR=${PIPESTATUS}

OUT=$(<${OBJECT_FILE}.warnings)

if ! [[ -z "$OUT" ]]; then
    # reprint the warning/error
    >&2 echo "${OUT}"
    echo "rm -f ${PWD}/${OBJECT_FILE}" >> @CMAKE_BINARY_DIR@/remove_obj_with_warnings.sh
else
    rm -f ${OBJECT_FILE}.warnings
fi

exit ${ERROR}

build.sh

#!/bin/bash

if ! [ -d build ]; then
    mkdir build
    cmake -H. -Bbuild -G "Ninja"
fi

if [ -f build/remove_obj_with_warnings.sh ]; then 
    sh ./build/remove_obj_with_warnings.sh
    rm build/remove_obj_with_warnings.sh
fi

cmake --build build

I thought collecting the files to be deleted in remove_obj_with_warnings.sh would be faster than grepping for the .warnings files. The disadvantage would be that it could contain files that are already deleted or not-yet-existing (covered by giving rm -f).

Especially true if you make the remove_obj_with_warnings.sh call optional.

References used:

  1. Make CMake use gccfilter
  2. Make gcc put relative filenames in debug information
  3. bash: redirect (and append) stdout and stderr to file and terminal and get proper exit status
  4. Handling gcc warnings and output in a Bash Script
Community
  • 1
  • 1
Florian
  • 39,996
  • 9
  • 133
  • 149