Save and reprint warnings for successfully-compile

2019-01-20 01:14发布

问题:

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.)

回答1:

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