On Dec 4, 2006, at 8:27 PM, Jim Wintermyre wrote: I've been seeing similar issues in our projects. It looks like in our case it is due to precompiled header usage. By default, XCode puts the precompiled headers into /Library/Caches/com.apple.Xcode.n/SharedPrecompiledHeaders/, where n is the current user ID. This cache folder periodically gets cleaned out, based on the prefs settings BuildSystemCacheSizeInMegabytes and BuildSystemMinimumRemovalAgeInHours (you can change these with the defaults tool, see Expert Preferences Notes from the Help menu for details). Depending on these settings, and how big the precompiled headers for your projects are, this could happen more frequently than you might expect.
This is all correct. Coming from CodeWarrior, I was really surprised to find this behavior. First, CW precompiled headers are generally stored in the project folder itself. This is handy for example if you happen to have your build tree(s) on a portable drive that moves between systems. And it means that things don't get rebuilt unless a header change necessitates it, or you manually remove object code. Also, the size of the CW precompiled headers seem to be WAY smaller than what XCode generates. For example, in one of our plugin projects the XCode precompiled header was about 40 MB, whereas the CW one was less than 4 MB. Even accounting for double compilation for universal binaries, that seems ludicrous. Now, doing a full build of our product, which consists or about 40 plugins, times 3 different plugin formats, plus a couple user apps and a framework, my precompiled header cache alone is about 10 GB, over twice the size of the entire source code tree (which also contains all the built products)!
gcc, unfortunately, was not designed by the FSF community to support precompiled headers, so the implementation is not as we would have designed it from scratch. A gcc precompiled header is essentially a memory dump of the gcc state after the processing of the last header file, and it is as big (and as fragile) as you mention. Specifically, it must have exactly the same compiler flags as the job being compiled in order to be used. Additionally, it seems that using the precompiled header at all may not result in that much speed savings, particularly with distributed builds, and/or quad processor machines. The PCH must be compiled by a single processor, and everything else has to wait for that to finish, whereas just having a non-precompiled prefix file can be included in multiple source files which compile concurrently. In CW, there was a much more dramatic difference in build time when using a PCH.
Well, the time with gcc is pretty dramatic as well; gcc without precomps can be 10x-20x slower than with precomps. Only in a few cases with very small sets of headers do we see precompiled headers cost more than they save. I think that I might have some setting incorrect. I have a subproject which builds a static lib. This lib is used by each of the plugins, and should only need to be built once. However, when I do a full build, my PCH cache ends up with as many copies of this PCH folder as there are plugins, which implies that something is causing XCode to think it needs to be rebuilt for each plugin?
You can reduce or eliminate this by making sure that your plugins share one prefix file and have as many common compiler flags as possible. Inside each .pch directory is a file that contains a list of the relevant compiler flags that generated it. Diff these files, and figure out what settings are different. If you can eliminate the differences, the pch will be shared between targets and reused repeatedly, rather than being regenerated. The biggest offender is Preprocessor Macros; move all of them if possible to Preprocessor Macros Not Used In Precomps. Each PCH folder has a name like "PluginLibrary-xxx" where xxx is some 28-letter lowercase hash. Could this be some weirdness due to using subprojects?
Nope, this is indeed a hash, and it's a first-line indication to Xcode whether it can reuse a precomp across projects. Xcode hashes the prefix file path and compiler flags, and if it finds a precomp that matches that hash, it uses it; if not, it generates a new one.
Chris |