
                        Using FlexeLint with GCC

These notes describe how to use the option file co-gcc.lnt which serves
as a replacement for the older co-gnu.lnt and co-gnu3.lnt options files.

                           --- Introduction ---

The configuration of Lint for use with GCC-compiled sources is complicated
by two major issues:

First, GCC defines a large number of preprocessor macros internally (i.e.,
it defines a lot of macros for which there are no '#define' directives in
any source file that Lint can read.).  Unless this issue is resolved for
each project, Lint will not see the same sequence of C/C++ tokens as GCC
when it tries to analyze your program, and as a result it will not see the
same set of declarations; consequently you'll see lots of spurious 
messages because the analysis will reflect that of a program that is
truly ill-formed (unlike the program seen by GCC).

Second, GCC is aware of several built-in functions.  Most of them can be
presented to Lint as ordinary forward-declarations of functions so as to
avoid undesired diagnostics claiming that these functions were not
declared before the first point of use.  Note, these declarations do not
necessarily need to be presented to GCC.

                 --- Solving the Preprocessor Problem ---

Before we get started, we'll need an empty C source file.  Call it
'empty.c' and save it to disk.  Next, use your favorite command
interpreter environment (such as sh in Unix-like environments or cmd.exe
on Windows) and go to the directory containing 'empty.c'.  Verify that you
can run GCC by doing the following:

        gcc -v

If your environment is properly set up you should see a version string.
(You may also want to verify that this is the same GCC executable used to
compile your sources;  check the PATH environment variable or try running
'which gcc'.)  

Assuming that worked, you can now try the following (and note that case,
as with C/C++ identifiers, is important.):

        gcc -E -dM empty.c

On one system, we then see definitions like:

        #define __DBL_MIN_EXP__ (-1021)
        #define __FLT_MIN__ 1.17549435e-38F
        #define __DEC64_DEN__ 0.000000000000001E-383DD

... followed by about a hundred more '#define' directives.  What just
happened?  We passed two options to gcc; the first one, '-E', means,
"don't run the compiler; just run the preprocessor".  The option '-dM'
(not to be confused with '-DM', which is completely different) means,
"don't generate preprocessor output; instead, only dump all macro
definitions, including those defined internally".

With this output, we are now halfway to the point of having a preprocessor
configuration for Lint.  First, let's redirect those macros to a file:

        gcc -E -dM empty.c >lint_cmac.h

Next, let's test our configuration:  make a simple C source file
containing the "Hello, world" program in a file called 't.c' (in the same
directory as 'empty.c')

        #include <stdio.h>
        int main () {
            printf( "hello, world!\n" );
        }
        
To lint this program, first copy the lnt file (co-gcc.lnt) and the
associated header (co-gcc.h) into the same directory.  Next, make a file
called 'std.lnt' which will (at first) contain only:
        
        co-gcc.lnt

This tells lint, "process the arguments in co-gcc.lnt".  Two of those
arguments are:

        -header(co-gcc.h) // #include's headers generated by GCC.
        +libh(co-gcc.h)   // Marks co-gcc.h as library code.
    
'-header(co-gcc.h)' means "behave as if each primary source file began
with '#include "co-gcc.h"'.  (Note that co-gcc.h includes "lint_cmac.h"
when Lint runs in C language mode.)

Finally, try running:

        /path/to/lint std.lnt t.c

... or, on Windows:

        [drive-letter]:\path\to\lint-nt std.lnt t.c

Next we'll see:

FlexeLint for C/C++ (Unix/386) Vers. 8.00w, Copyright Gimpel Software
1985-2007

--- Module:   t.c (C)
         _
#include <stdio.h>
         t.c  3  Error 322: Unable to open include file 'stdio.h'

What happened?  We forgot the other half of the preprocessor
configuration: specifying the list of directories to search for system
headers.  Fortunately GCC already knows this list and gives us a way to
discover it.  Just run:

        gcc -c -v empty.c

On one system (specifically, Mac OS X on Intel), that yields a lot of
verbose output including these lines:

        #include "..." search starts here:
        #include <...> search starts here:
         /usr/local/include
         /usr/lib/gcc/i686-apple-darwin8/4.0.1/include
         /usr/include
         /System/Library/Frameworks
         /Library/Frameworks
        End of search list.

(Naturally, that list will look a bit different in different
environments.)  To make Lint search for system headers in the same way, we
need to take that output from GCC and use it to make a set of --i[dir]
options.  E.g. on the same system, that means I should change std.lnt to:

        --i/usr/local/include
        --i/usr/lib/gcc/i686-apple-darwin8/4.0.1/include
        --i/usr/include
        --i/System/Library/Frameworks
        --i/Library/Frameworks
        co-gcc.lnt

(Note, arguments to Lint are processed in order, so the --i options must
appear before t.c or they will not take effect in time.)

Now let's try linting "hello, world" again:

        /path/to/lint std.lnt t.c

This time we should see no error messages.  Are we done yet?  Not quite.
First, let's test our configuration against all of the C Standard Library
headers.  Modify t.c to:

        #include <assert.h>
        #include <iso646.h>
        #include <setjmp.h>
        #include <stdio.h>
        #include <time.h>
        #include <ctype.h>
        #include <limits.h>
        #include <signal.h>
        #include <stdlib.h>
        #include <wchar.h>
        #include <errno.h>
        #include <locale.h>
        #include <stdarg.h>
        #include <string.h>
        #include <wctype.h>
        #include <float.h>
        #include <math.h>
        #include <stddef.h>

        int main () {
            printf( "hello, world!\n" );
        }


... and try linting again:

        /path/to/lint std.lnt t.c

This time you should see a number of messages; in particular, you should
see Info 766 issued for most of these headers since they were not used.
you may also see some legitimate warnings, and you may see an Error or two
about 'wchar_t'.  (Note, if it seems as though your port of GCC recognizes
wchar_t as a keyword instead of an identifier then uncomment the use of
'+fwc' in your copy of co-gcc.lnt).

[NOTE: we should point out that the option -wlib(1) is in co-gcc.lnt for a
reason.  Please note that we *strongly* recommend against the use of
options like -wlib(0) or -elib(*).  If you see syntax error messages about
library header code, odds are that something is wrong with the Lint
configuration; so you'll do yourself no favors by silencing Lint when it
sees constructs that Lint's parser doesn't know how to handle.  If you need
help with correcting your configuration, please check the Lint manual or
contact us.]

We should really be done now, right?  Well, not quite.  There is one issue
remaining:  namely, the set of predefined macros (dumped into "lint_cmac.h"
earlier) depends not only on things like the target system and the version
of GCC; it also depends on the options that you pass to GCC when you
compile your program.  E.g., if we invoke:

        gcc -O3 -E -dM empty.c  >lint_cmac.h

... then (with the version of GCC we tested) lint_cmac.h will no longer
contain a definition for the macro '__NO_INLINE__' and contains a new
definition for '__OPTIMIZE__'.  So when you generate macros, you should be
careful to pass in the full set of options used when you compile.
Ideally, you should establish a target in your build system that
re-generates the predefined macro header whenever your build configuration
changes; that way you'll seldom need to think about it again and Lint's
preprocessor configuration will always match the compiler's.

Assuming you've generated the right set of predefined macros for your
build settings, you should now try Linting a single primary source file in
your project.  Since we're just starting out, let's run with -w1 (i.e.,
with the warning level set to one) so that we can deal with any syntax
errors that pop up:

        lint std.lnt -u -w1 some-project-file.c

Again, remember that syntax errors at this stage are likely due to a
misconfiguration; please check the lint manual for likely remedies or
contact us if the solution is not obvious.

Once you've taken care of all syntax errors, try doing the same with all
project files.  We recommend placing the name of each project file in a
.lnt file (often called project.lnt); e.g.: 

        file1.c
        file2.c
        [...etc.]

... and invoke Lint like so:

        lint std.lnt -w1 project.lnt

When all syntax errors are resolved, you can begin turning on Warning
messages (i.e., those listed in section 17.4 of the Lint manual).  You can
turn them on individually after '-w1' or you can instead use '-w2' and
then suppress the Warnings that are less severe.  (For details on message
suppression, see section 5.2 of the Lint manual.)

At this point, you should be well-equipped to Lint any C program compiled
with GCC.  However, C++ users have a couple more points to consider.

The command used to invoke GCC also affects the set of predefined macros.
Observe the difference in '#define' output when you invoke:

        g++ -O3 -E -dM empty.c >lint_cppmac.h
        diff lint_cmac.h lint_cppmac.h

Also, note that the list of directories to search for Standard Library
headers has some new additions when you use 'g++ -v -c empty.c' instead of
'gcc -v -c empty.c'.  Your sequence of --i options should be set
accordingly.

That's about it for the preprocessor.  If you find this tutorial to be
lacking in some way, please contact us (support@gimpel.com) with your
suggestions for improvement.

