Build setting inheritance

Managing build settings in Xcode has always been a bit fiddley. You can have per-project build settings (called "Configurations"), and per-target settings (simply called "Build settings") which inherit from the per-project settings and can be used to override them. Things can get fairly complicated very quickly when you have multiple configurations, multiple targets and multiple projects. That’s not even mentioning the possibility of passing settings on the command-line or in the environment, nor of setting compiler flags on a per-file basis.

One of the problems is that there are so many settings and the UI doesn’t make it easy to feel sure that you’ve got your settings consistently applied across projects. Another is that if you’ve ever been struck down by an Xcode project file corruption bug you won’t have a nice time at all trying to recreate your project from scratch and get all the settings right.

But one of my key gripes with configurations is that mostly they consist of standard settings with a few deviations from the default. Your "Debug" configuration is most likely exactly like your "Release" configuration, for example, with a few small changes. But sometimes you want to make a change that should apply to both: do you edit them both? Or do you apply the setting on a per-target basis? If you have lots of targets then editing the setting in two configurations will be easier than editing it in multiple targes. If you have lots of configurations (let’s say you have "Profiling", "Code coverage" and "Beta testing" in addition to "Debug" and "Release") but only one target then perhaps editing it there will be easier. Often you find yourself making the same changes over and over again in every single new project that you start (for example, I almost always end up setting GCC_C_LANGUAGE_STANDARD to GNU99).

Just today I was thinking how great it would be if configuration settings were capable of inheriting from each other. That is, you could define your "Release" configuration and you could define your "Debug" configuration as being based on "Release" with some overrides. "Debug" would inherit from "Release"; make a change to "Release" and it would be automatically inherited by "Debug" unless you explicitly override it.

I was about to file a feature request for this when I stumbled across something that I had overlooked or filed away in the mental "look-at-it-later" basket. It turns out that you already can do pretty much exactly what I am talking about. By using what Apple calls "Configuration files" (can’t they have come up with a less generic name?) which end in an xcconfig extension you can define a set of build settings and then inherit from them in any project-level configuration, and in any per-target build settings. Even better, because these are plain text files you can store common settings in them and then use the same configuration file across multiple projects. Change that one file and al the projects automatically inherit the change. You can even include other configuration files via an include directive, effectively allowing you to build up an Object-Oriented-like inheritance hierarchy, and it’s all plain text so it plays nicely with version control and makes it very easy to see build settings at a glance.

Configuration files neatly take care of one of the main problems with the build settings interface. Instead of seeing a confusing mess of bold settings (overrides) and unbolded settings (inherited or default values) you can now set your view to "Customized settings" and you’ll get a nice uncluttered view with only a few settings in it. Without configuration files it can be time consuming to tell exactly where build settings are coming from: are they overridden at the project or target level? With configuration files it’s that much easier to see at a glance what’s really going on.

And with Apple’s typical attention to detail and UI polish, setting up an xcconfig file is a breeze. Just select build settings in the Xcode build settings view, copy, and paste into a text file: the key/value pairs are translated for you. It’s funny how sometimes a slick Aqua GUI isn’t the best was for working with information; in this case a plain old text file turns out to be better (by way of a counterexample consider the complexity of makefiles and how Xcode’s elegant GUI neatly disposes of the underlying complexity of dependency graphs.) The build configurations implementation is one example of the wedding of both ways: the lickable Aqua on top and the plain and simple text files underneath. Neither on its own is perfect but add them together and you have something that works great.

This is great. I wish I’d taken notice of this sooner.