When you hit some level of code size in a project you starting to observe the following sequence:
- Developer creates and tests a feature
- Before submitting commit to repository update/fetch/sync is done
- Developer builds project again to check if build/basic functionality is not broken
- Smoke tests
- Submit
During step 3 you hear "damn slow rebuild!". One discovers that synchronization with repository forces him to rebuild 20% of files in a project (and it takes time when project is really huge). Why?
The answer here is: header dependencies. Some header files are included (directly and indirectly) in many source code files, that's rebuild of so many files is needed. You have the following options:
- Skip build dependencies and pray resulting build is stable / working at all
- Reduce header dependencies
I'll explain second option.
The first thing to do is to locate problematic headers. Here's a script that will find most problematic headers:
#!/bin/sh awk -v F=$1 ' /^# *include/ { a=$0; sub(/[^<"]*[<"]/, "", a); sub(/[>"].*/, "", a); uses[a]++; f=FILENAME; sub(/.*\//, "", f); incl[a]=incl[a] f " "; } function compute_includes(f, located, arr, n, i, sum) { # print "compute_includes(" f ")" if (f ~ /\.c/) { if (f in located) { return 0 } else { located[f] = 1 return 1 } } if (!(f in incl)) { return 0 } # print f "->" incl[f] n = split(incl[f], arr) sum = 0 for (i=1; i<=n; i++) { if (f != arr[i]) { sum += compute_includes(arr[i], located) } } return sum } END { for (a in incl) { n = compute_includes(a, located) if (F) { if (F in located && a !~ /^Q/) { print n, a } } else { if (n && a !~ /^Q/) { print n, a } } for (b in located) { delete located[b] } }; } ' `find . -name \*.cpp -o -name \*.h -o -name \*.c` \ | sort -n
Sample output:
266 HiddenChannelsDefinitions.h 266 nmc-hal/hallogger.h 268 favoriteitemdefinitions.h 270 nmc-hal/playback.h 279 pvrsettingsitemdefinitions.h 279 subscriberinfoquerier.h 280 isubscriberinfoquerier.h 286 notset.h 292 asserts.h
As you can see there are header files that require ~300 source files to be rebuilt after change. You can start optimisations with those files.
If you locate headers to start with you can use the following techniques:
- Use forwad declaration (class XYZ;) instead of header inclusion (#include "XYZ.h") when possible
- Split large header files into smaller ones, rearrange includes
- Use PIMPL to split interfaces from implementations