There are many program analyses that are more effective when done on the whole program.
The merger is a tool that combines all of the C source files in a project into a single C file. There are two tasks that a merger must perform:
For the first task the merger impersonates a compiler and a linker (both a GCC and a Microsoft Visual C mode are supported) and it expects to be invoked (from a build script or a Makefile) on all sources of the project. When invoked to compile a source the merger just preprocesses the source and saves the result using the name of the requested object file. By preprocessing at this time the merger is able to take into account variations in the command line arguments that affect preprocessing of different source files.
When the merger is invoked to link a number of object files it collects the preprocessed sources that were stored with the names of the object files, and invokes the merger proper. Note that arguments that affect the compilation or linking must be the same for all source files.
For the second task, the merger essentially concatenates the preprocessed sources with care to rename conflicting file-local declarations (we call this process alpha-conversion of a file). The merger also attempts to remove duplicate global declarations and definitions. Specifically the following actions are taken:
Here is an example of using the merger:
The contents of file1.c is:
struct foo; // Forward declaration extern struct foo *global;
The contents of file2.c is:
struct bar { int x; struct bar *next; }; extern struct bar *global; struct foo { int y; }; extern struct foo another; void main() { }
There are several ways in which one might create an executable from these files:
gcc file1.c file2.c -o a.out
gcc -c file1.c -o file1.o gcc -c file2.c -o file2.o ld file1.o file2.o -o a.out
gcc -c file1.c -o file1.o gcc -c file2.c -o file2.o ar r libfile2.a file2.o gcc file1.o libfile2.a -o a.out
gcc -c file1.c -o file1.o gcc -c file2.c -o file2.o ar r libfile2.a file2.o gcc file1.o -lfile2 -o a.out
In each of the cases above you must replace all occurrences of gcc and ld with cilly --merge, and all occurrences of ar with cilly --merge --mode=AR. It is very important that the --merge flag be used throughout the build process. If you want to see the merged source file you must also pass the --keepmerged flag to the linking phase.
The result of merging file1.c and file2.c is:
// from file1.c struct foo; // Forward declaration extern struct foo *global; // from file2.c struct foo { int x; struct foo *next; }; struct foo___1 { int y; }; extern struct foo___1 another;