Building the Build System – Part 1 – Abandoning the Build Panel
XCode has a decent build system, but it doesn’t work as well as it could out of the box. With just a little work, you can make your projects easier to setup and maintain just the way you want them, improve your code, and even speed up your build times.
The first thing we want to do is get rid of one of the great obfuscations of Xcode: The Build panel.

The build panel seems convenient at first, but in practice it makes it hard to see what’s going on in your build. It especially gets confusing as your build settings get complicated. When you need to turn off Thumb Code Generation because of an obscure assembler conflict in legacy C++ code (true story), it would be nice to put a comment somewhere indicating why you’ve done this so someone doesn’t come along later switch the setting. The Build Panel doesn’t give you an easy way to include comments right along with the setting (the “Comments” panel is pretty useless in my experience), and it’s easy to lose settings or accidentally apply them to only to one configuration.
XCode provides a better solution called xcconfig files. Everything you can do in the build panel can be done in xcconfig files, and you can actually read them and make comments. So let’s make some.
- Create a new Window-Based iPhone Application. (Everything we do here works exactly the same for Mac.)
- Add a new group to your Groups & Files called “Build Configuration”.
- Add a new file to the group. Under “Other” select “Configuration Settings File.” Call it “Shared.xcconfig”.
- Create three more xcconfig files called Debug, Release and Application.
- Double-click the Project to open the Project Info panel, and select Build.
- Select Configuration: “Debug” and Show: “Settings Defined at This Level.”
- Select all the settings (you can use Cmd-A here).
- Copy them with Cmd-C.
- Go back to Debug.xcconfig and paste with Cmd-V. You now know how to find out the xcconfig syntax for any Build Panel setting you’re uncertain of.
- Go back to the Build Panel and hit Delete to set all settings to default. Select “Show: All Settings” so you can see your settings again.
- In the lower-right of the panel, for “Based On:” select “Debug.” You should see your old settings show back up, but not bold this time.
- Repeat for Configuration: “Release”. Copy them to Release.xcconfig and delete them from the Build Panel.
- Double-click on the Target, and repeat the process, moving both its Debug and Release settings to Application. Put in a comment to indicate which are the Debug settings and which are the Release settings. We’ll clean them up shortly. Select “Configuration: All Configurations” and “Based On: Application.”
We’ve now moved everything from the build panel to the xcconfig files, and the system should build. Debug and Release are slightly confused because we mixed the settings in Application, but we’ll fix that now.
Look in Application.xcconfig, and move anything that’s in Debug but not in Release to Debug.xcconfig, and anything in Release but not Debug to Release.xcconfig. Anything that’s in both, delete the second copy of.
Anything that is in both Release and Debug, move to Shared, and put #include "Shared.xcconfig" at the top of the Release and Debug configs.
If you’ve followed all the instructions, you should have four files that look like this (assuming your product’s name is “Test”):
Shared.xcconfig
ARCHS = $(ARCHS_STANDARD_32_BIT) SDKROOT = iphoneos2.2.1 CODE_SIGN_IDENTITY = CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer PREBINDING = NO GCC_C_LANGUAGE_STANDARD = c99 GCC_WARN_ABOUT_RETURN_TYPE = YES GCC_WARN_UNUSED_VARIABLE = YES
Release.xcconfig
include "Shared.xcconfig"
COPY_PHASE_STRIP = YES
Debug.xcconfig
include "Shared.xcconfig"
ONLY_ACTIVE_ARCH = YES COPY_PHASE_STRIP = NO GCC_DYNAMIC_NO_PIC = NO GCC_OPTIMIZATION_LEVEL = 0
Application.xcconfig
INFOPLIST_FILE = Info.plist PRODUCT_NAME = Test ALWAYS_SEARCH_USER_PATHS = NO GCC_PRECOMPILE_PREFIX_HEADER = YES GCC_PREFIX_HEADER = Test_Prefix.pch
If you Build&Run now, everything should work. It’s not a great build configuration, but it’s Apple’s default in a form that we can start understanding and improving.
But before that, we’re going to convert this to a Project Template, so we don’t have to go through this process again. We’ll do that in the next part of this series.
Here the situation we have and were wondering your advice/opinion, we have a project which uses many libraries and we build multiple configurations (10 for adhoc, 10 for distribution), we have a project which includes the source code for all libararies and then builds each as a static library and links all together, after adding Configuration the project loading and especially switching between Simulator and Device is extremely slow, also accessing any project settings is a hog.
Static libraries share a lot of the preprocessors, the idea is to decouple static libraries into the different project files and include those in the main project. But here is the Challenge, some libraries require Macros to be set depending on which product we are building. i.e. Configuration A, sets Macro USE_SPECIAL_CASE, which a few static libraries needs to compile with, Configuration B, sets USE_SPECIAL_CASE2 etc.
We have tried to add GCC_PREPROCESSOR_DEFINITIONS = $(GCC_PREPROCESSOR_DEFINITIONS) USE_SPECIAL_CASE2 but it is not propagated to other projects.
Thanks for the help.
First, read over the Build Settings page in the docs to make sure you understand how layering and setting evaluation work. In particular note how you can use both Project-level and Target-level settings to compliment each other.
More specific to your issue, you almost certainly want to use #include files. So you put the common settings in some SharedLibrary.xcconfig and you then #include that file into each project’s xcconfig. You can overload settings after you’ve done the #include. In the case of GCC_PREPROCESSOR_DEFINITIONS above, make sure that you’ve defined that in something you’ve #included. That would be my suspicion.
Also, remember $(inherited), which represents the value this variable has based on higher (rather than earlier) levels. What I mean is that if you set VAR to something and then #include that, $(VAR) will give you the value it was given in the earlier #include. If you set VAR at the Project level, then at the Target level $(inherited) will give you the value from the Project level. So often the first setter of VAR at a level should use $(inherited), and later setters should use $(VAR) as their first element.
Remember that each project compiles independently. What you’ve done in the “parent” project has almost no impact on the “children” projects (there really is no such thing as “parent” and “children” in xcodebuild). I say “almost no impact” because a couple of things are passed down; specifically the SDK and the Configuration. But setting GCC_PREPROCESSOR_DEFINITIONS in one project has no impact at all on other projects. If you want to share a value, you need to hoist that into a separate xcconfig file and #include it into the xcconfig files for each project.
Right i see, i assumed that it maybe a problem. Is there a way to query what Configuration has been choosen, cause what we have is 10 Adhoc Configuraitons and 10 Distributions, so what i was thinking to make
adhoc1.xconfig which would Configuration 1 be based on, if Configuration is passed to other project, in the shared.xcconfig i may have something like :
if $(CONFIGURATION) == ADHOC1
// set macros
else if
etc etc, but i am not sure if it is possible to query which configuration has been chosen in xcconfig and set settings dynamically.
Or maybe you have some other ideas you don’t mind to share?
thanks a lot.
You can’t do if() logic like this on configurations. The only conditionals you can do are architecture, SDK and variant (I’ve never really found a good way to make variant useful).
The way you create conditionals is to have separate files and base a Configuration off of that file (at the Project or Target level). So rather than having Shared.xcconfig set the macros, you set the macros in adhoc1.xcconfig and #include Shared.xcconfig there to get the rest. You can see an example of this in my template project. I have Shared.xcconfig (which is included by others, but no configuration is based on it), then I have Debug.xcconfig which the Debug Configuration is based on at the Project level, Release.xcconfig which the Release Configuration is based on at the Project level, and Application.xcconfig which all configurations are based on at the Target level. So Shared is read, then either Debug or Release overrides settings in Shared, and finally Application overrides settings in Shared, Debug or Release.
I’m not certain what “10 Distributions” mean. Do you mean 10 targets, or 10 adhoc configurations and 10 distribution configurations (20 configs in total). If 20 configs, then you’d do exactly as I’ve done, but instead of just 2 (“Debug” and “Release”) you’d have 20.
@ramzez Another question , is that possible to define Configuration as part of the xcconfig ?
@Rob Napier yeah that’s correct, we have 10 Distribution Configuraitons and 10 Adhoc Configurations. I understood your approach and did the same, but then hit the problem described above. It is pity XCode doesn’t have Parent>Child relationship in that case everything would be so perfect.
@ramzez You can’t define Configuration in the xcconfig, because the appropriate xcconfig file is chosen by the Configuration.
So in your case, there would probably be a Shared.xcconfig that is shared by every project in the system. Then there would be 20 “configuration” xcconfigs that would #import Shared and would also be shared by every project in the system. At the Project level of every project, you would make 20 configurations, and base them on each of these 20 xcconfig files. That’s a bit complex, I know, and very manual if you have to add a new configuration, but it is the best solution I know of. You can Applescript it, though. Here’s the idea to get you started:
Then each project would have its own xcconfig that all configurations are based on at the Target level. At this level, you can do things like:
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) USE_SPECIAL_CASE2
Note the use of $(inherited) here; we’re doing it at the Target level, so we want to inherit from the Project level.
Yeah that’s an interesting approach and unbelievable short comming of IDE, if i have time i would have a look at it. Interstingly all other platforms we work with don’t have such limitation. I just used bugreports to ask for Parent-Child relationship as a new Feature.
Thank you very much for your help and time!