PATH
You create a precompiled header by passing the -precomp switch to cc . Depending on the context(s) in which the header is used, -D switches should also be passed to cc , as explained below.
% cc -precomp foo.h -o foo.p
We say a header is "context dependent" if the definitions in the header may change depending on the context in which it is included. Most uses of conditional compilation and macro expansions cause context dependence. For instance, the following header is context dependent:
#ifdef DEBUG
int a;
#else
int b;
#endif
The context at any point is determined by the macros that are defined there. A precompiled header must be created in a context equivalent to that where it is used. By passing switches to the preprocessor, any set of macros can be predefined, creating a context in which the precompiled header is built. This is done by passing a -D switch for each macro in the context.
A precompiled header built from system headers typically requires no -D switches, because programmers usually include system headers in a context-independent way. For instance, the public Application Kit headers contain almost no preprocessor conditionals; clients cannot change declarations in headers by defining macros. So the command to build a precompiled header from AppKit.h is:
% cc -precomp AppKit.h -o AppKit.p -arch i386 -arch ppc
(The architectures affected are usually specified using the -arch switch: "fat" compilation requires "fat" precompilation.) But if you must use a header bar.h in a context where FOO is defined, you should build the precompiled header as follows:
% cc -precomp -DFOO bar.h -o bar.p
You should also pass any preprocessor switches, such as -I , that you use in your project.
By making precompiled headers bigger (that is, containing more headers), a given C file may include fewer precompiled headers, and will generally compile faster. However, the bigger a precompiled header is, the more likely that name conflicts will occur.
For example, if you were to combine all the headers for a project, including system headers, into a single precompiled header, it is possible that there would be a name conflict. There may be a macro defined that happens to match one of your local identifiers, or there may be a public struct declared that happens to match one of your private struct names. Such conflicts manifest themselves as preprocessing errors, syntax errors, or semantic errors. The conflicts may be resolved by renaming identifiers, or removing a conflicting header from the precompiled header.
Another disadvantage to big precompiled headers is file dependencies. If all of the C files in a project depend on a single precompiled header which in turn depends on all headers in the project, then changing a header requires recompilation of the entire project. A better approach is to build a precompiled header containing all the system headers used by a project, and perhaps also a separate precompiled header for the local headers in the project. We recommend that during development, while local headers are changing, precompiled headers be used only for system files. When local headers have stabilized, they may be combined into a precompiled header.
A precompiled header is dependent on all the files it includes. A make dependency rule can be constructed similar to the way rules are constructed for source files. The following rule builds a precompiled header from a header:
.h.p:
cc -precomp $(CFLAGS) $*.h -o $*.p
A precompiled header records absolute path names for all the headers that went into it. These paths are then checked when the precompiled header is used. Therefore a precompiled header should be built in the same directory in which it is to be used, and all the headers that went into the precompiled header must not be moved or modified.