A C or C++ program always includes header files that are provided by the compiler or the operating system. When they are instrumented by Coco, sometimes unexpected error messages occur.
This post explains why this happens, when this happens, and what to do about it.
A Concrete Example
mingw32-make: Entering directory 'C:/Users/Someone/textedit/textedit_v1'
csg++ -c -o textedit.o textedit.cpp
c:/mingw32/include/c++/bits/random.h:73: col 44: Warning (Squish Coco): syntax error, unexpected ';', expecting '}'
c:/mingw32/include/c++/bits/random.h:117: col 45: Warning (Squish Coco): syntax error, unexpected ';', expecting '}'
c:/mingw32/include/c++/bits/random.h:200: col 4: Warning (Squish Coco): syntax error, unexpected '}', expecting $end
c:/mingw32/include/c++/bits/random.h:6066: col 2: Warning (Squish Coco): syntax error, unexpected '}', expecting $end
More error messages follow, and the compilation fails:
<code>Fatal Error (Squish Coco): Could not generate object file</code>
This is a very specific kind of failure. That is, the error message does not occur in the source files of the project.
One can see in the first line of the compilation log that the project resides in the directory C:\Users\Someone\textedit, but the error occurs in the file C:\mingw32\include\c++\bits\random.h, which is outside the project. The file is instead a header file of the C++ standard library that comes with MinGW.
Related to this is another notable difference to other Coco errors: the error is compiler-specific. When the same project is compiled with, for example, Visual Studio, the error does not show up.
How to Handle it
As in many cases in which Coco cannot handle a file, we must exclude it from instrumentation. As we will soon see, it is better to exclude all system header files. The error messages give us the information we need.
In the example, MinGW is installed in the directory C:\mingw32, and the paths to all its system header files begin with "C:\mingw32\include". The simplest way to exclude these files is therefore to add a compilation option
But why is this necessary? And why can't Coco handle this case on its own?
Files that are Excluded
The answer is that in most cases, Coco does exclude system header files on its own. But to exclude them, Coco needs to know where they are.
In the following situations, the location of the system system header files is known to Coco and they are excluded automatically:
- Under Unix, when a built-in compiler like gcc or clang is used. The system headers are then in the directory /usr/include and its subdirectories are excluded automatically.
- Under Windows, when Visual Studio or MSBuild are used. Coco then knows how to find the system header files that come with the compiler and how to exclude them.
For other build systems, the system directories must be given explicitly. The most common cases are:
- MinGW, as we have seen, and,
- Most cross-compiler toolkits, under Linux and also under Windows.
For cross-compiler toolkits it is often also necessary to create a special version of the compiler wrapper. The documentation shows how this is done.
Why System Header Files Must Be Excluded
We have not yet explained why files like random.h cause an error.
This is a trade-off in the design of Coco. System header files may contain compiler-specific code or syntax that is never used in application code. One could extend Coco's parser to handle these cases, but it would be:
- An enormous effort, because the work needs to be done for every compiler, and,
- Unnecessary, since the goal of a coverage measurement tool is to measure the coverage of an application and not of the system libraries.
Coco is therefore built to work with standard C and C++ code, and it automatically tries to exclude the system header files. The only disadvantage of such an approach is that sometimes Coco cannot find the header files.
What Happens When a File is Excluded
When Coco is instructed to exclude a file from instrumentation, the CoverageScanner reads it in an accelerated mode. It does not parse it and does not insert any instrumentation code in it.
When a header file is excluded, it is ignored in all cases in which it is read by the compiler, in reaction to an #include statement.
Other Reasons to Exclude Files
Even if a file can be parsed by Coco, it may be a good idea to exclude it. In C++, this is especially true for template libraries like the Standard Template Library. Such a library contains template code for data structures, like std::vector or std::map. These classes are built for efficiency, and when they are instrumented, the additional code may slow down the instrumented program considerably. This is the other reason why in the example we excluded the whole MinGW library directory tree and not just a few files with unusual syntax.
The same is true for C and C++ libraries in general. Some of them contain template data structures and most contain inline code. (Note that newer versions of C allow an inline statement too). They will be instrumented by default and may slow down the program; in every case they will show up in the coverage report even if they do not belong there.
- C# does not have an include statement, therefore the problem does not occur.
- For QML coverage, the Qt library files are excluded by default. When the environment variable COCOQML_DEFAULT_BLACKLIST is set to 0, they are included again.