Self-contained sources/headers

the juce sources seem to be written in a rather, odd way: c++ files don’t include the headers they’re actually using, but they are expected to be included from a unity-build file. while this works (as in “it compiles”), i find it incredibly hard to work with the sources, as it breaks the code model of IDEs, which index source files, so i need to resort to full-text search, which feels pretty 90s.
in a way it would be nice if the codebase could be changed in a way that every file can be compiled in isolation.

Doing so would significantly increase the compilation time! What IDE are you using that struggles with it? I predominately use Xcode but I’m sure Visual Studio is fine with it too.

Doing so would significantly increase the compilation time

in the 1990s this might have been the case, but in times of #pragma once this statement is highly questionable (probably wrong): these days compilers even detect inclusion guards and avoid reparsing. furthermore we’re in the age of ssds, where disk access is much faster than with rotating hard drives.
if preprocessor performance affects compile times of c++ code in 2020, the code probably has more severe problems.

What IDE are you using that struggles with it?

mainly qtcreator, but vscode and clion seem to have similar problems

I’ve certainly tested it in the last few years. pragma once won’t help because the issue is that the same headers get included by lot’s of compilation units. Plus all the additional linkage time. In my experience unity builds can significantly improve build times. This is fairly recent article on the subject https://onqtam.com/programming/2018-07-07-unity-builds/

pragma once won’t help because the issue is that the same headers get included by lot’s of compilation units

sure, you only want to include the files that you use, but you want to include the files that you use

In my experience unity builds can significantly improve build times

we’re not arguing about pros and cons of unity builds, but about sources that compile in isolation:


this is bad:

// foo.h
class foo
{
  foo();
};

// foo.cpp
foo::foo()
{}

// lib.cpp
#include "foo.h"
#include "foo.cpp"

while in contrast, this is good:

// foo.h
#pragma once
class foo
{
  foo();
};

// foo.cpp
#include "foo.h"
foo::foo()
{}

// lib.cpp
#include "foo.h"
#include "foo.cpp"

there’s no change in compile times for lib.cpp, but foo.cpp can be parsed or compiled or evaluated via a static analyzer in isolation.

I see your point, keep unity builds but still allow individual files to be compiled. I guess the difficultly here would be all of the workarounds and header order required on specific platforms. I imagine this would be a significant undertaking for the JUCE team. I suspect a lot of refactoring would be required to prevent repetition.

I guess the difficultly here would be all of the workarounds and header order required on specific platforms.

hmm, i rarely came across well-designed code where header order mattered. especially when following the best practices that every file (header or source) should compile in isolation and no source depends on transitive headers (iirc bloomberg’s static analysis tools would be able to flag this)

You might both be aware, but I thought to point out, that not each cpp file is compiled in isolation, but each module is collected in a single TU. I assume this is due to the nature Projucer setup the build projects for the different IDEs. It allows the Projucer to work without knowledge of the individual files inside the module.
The drawback for parallelisation is probably negligible given that the number of modules is probably greater than the number of cores.

Now having all headers in a common include file has the advantage, it is only parsed once module.

Disclaimer: not an expert in compile times and definitely no fan of dev-ops jobs.

2 Likes

You might both be aware, but I thought to point out, that not each cpp file is compiled in isolation, but each module is collected in a single TU.

just to reiterate: i’m not voting against unity builds (i’ve introduced them myself to several codebases, which are one or two orders of magnitude larger than juce)

i’m just arguing that the codebase would be we way more robust / IDE friendly / composeable … if each header file and each source file could be parsed or compiled in isolation. in a well-designed codebase each source or header includes those sources or headers which are used (not more, not less).

I would normally agree but it’s just a complexity of some of the platform dependant headers (not JUCE specifically) because of naming collisions, macros, the disabling of the warnings caused by external headers etc. I guess juce_core would be the one that is likely the hardest to do. I think this would be a huge time sink with potential for lots of negative side effects!

I would normally agree but it’s just a complexity of some of the platform dependant headers (not JUCE specifically) because of naming collisions, macros, the disabling of the warnings caused by external headers etc.

out of curiosity: why should juce be different than any other codebase?