3
votes

I am playing around with a clang++ command line in order to learn how precompiled headers work. I've got something like this:

clang++ [several lines of options] sourcefile.cpp -o sourcefile.o -include durr.h -include hurr.h

where the two headers included via the command line have been precompiled into corresponding .h.pch files.

If I "-include" just one of the two headers, compilation succeeds and is faster than it is when I include neither, in the normal fashion for precompiled headers. But when I include both (as above), I get this error:

clang: warning: precompiled header 'hurr.h.pch' was ignored because '-include hurr.h' is not first '-include'

Is there any way (not necessarily using -include) to use multiple .h.pch precompiled header files to speed compilation of one .cpp file? I understand that such a feature would be seriously complicated by the tendency of the preprocessor to cause headers to affect one another (even if only via include guards). I don't really expect what I want to be supported, now that I've thought about it a little. But I'm trying to confirm here. The above error message is suggestive but not comprehensive, and the Clang user manual didn't seem to tell me the answer....

1

1 Answers

1
votes

It turns out there is: Clang supports "chained" PCH files, which is a feature that allows one PCH to represent an extension of another one. If the latter is included during some later compilation, then both it and the PCH that it depends on will be used to speed compilation. I think.

Something like this might produce an ordinary PCH:

clang++ -x c++-header header1.h -o header1.h.pch

And, if I'm understanding correctly (questionable), then something like this would produce a chained PCH that would extend header1.h.pch:

clang++ -x c++-header header2.h -o header2.h.pch -include-pch header1.h.pch

And then that chain of two PCH files can be used to speed compilation like so:

clang++ source.cpp -o source.o -include-pch header2.h.pch

(The parent PCH does not need to be mentioned in the command; header2.h.pch already knows where to find it I think.)

I haven't found any way to explicitly demand this sort of "chaining" via the command line. Simply including a PCH when compiling another PCH seems to produce a chained PCH... probably. My evidence that it does is mainly that in cases where header2.h actually includes header1.h, this technique seems to produce a small header2.h.pch and a large header1.h.pch, even though one would expect that if chaining weren't happening then header2.h.pch would generally be larger than header1.h.pch, since it contains at least as much information. This seems to match what I understand the purpose of PCH chaining to be: It saves resources by storing a dependent PCH's duplicate information as a cheap reference to the contents of another PCH.

My casual exploration suggests that a dependent PCH may itself have another one depending on it, extending the chain to three or more steps, though I'm not certain of this. When I tried to extend a chain involving real code by something like fifteen or twenty links, the PCH file sizes eventually blew up in what appeared to be an erroneous way, approximately doubling with each step. This eventually produced 300 MB PCH files for headers that, if compiled into PCHs without any attempt at chaining, would produce files smaller than 6 MB in size. This happened in Clang 3.6.something, and in Clang 3.7.0. I imagine it's a bug but who knows. I gave up on my exploration before reaching the point where I cared to try to pin it down and report it. And maybe the PCH chains aren't intended to ever grow very long anyway. This feature doesn't seem to be something people normally use....

Regardless, there seemed to be no way to do what I really wanted: mixing any two arbitrary PCHs, as long as they didn't directly conflict with one another, regardless of how they had been created. Chaining only allows two PCHs to be used together if one depends on the other. I had been interested in speeding compilation by making a PCH for each header in a project and then mixing groups of PCHs together during compilation as appropriate. But accomplishing this with chained PCHs seems to require making a tree of PCH files in which more than one PCH may correspond to a single header. I actually attempted to generate such a thing automatically, and seemed to succeed... but the above-mentioned "error" (if error it was) bogged me down, and to the extent that I did succeed, the time savings were not impressive enough to warrant continuing.

There is also some kind of "module" system in Clang that may be relevant here. But I have the impression that trying to exploit this to achieve the effect I want, if it could even be successful at all, would probably require me to change my source code to something special, and maybe to something non-standard. I didn't look into it much though, so maybe not. But anyway I guess standard C++ will probably get modules eventually and then all of this mess will (I hope) become a thing of the past.

GCC did not seem to support anything related to what I wanted, incidentally.