 When you hit some level of code size in a project you starting to observe the following sequence:
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