Xcode: Inherit `configuration`s from parent project - not possible?
Asked Answered
V

1

16

TL;DR

Is it possible to inherit custom configurations from parent project? No, not asking about inheriting some target setting.

Configurations and preprocessor macros/flags

Every project in Xcode is initialized with the two standard configurations called Debug and Release. It is a quite common pattern to setup a DEBUG flag using the setting under the display name Preprocessor Macros in Build Settings for a target (in .pbxproj this is called GCC_PREPROCESSOR_DEFINITIONS), which can be read from Objective-C code like this.

#ifdef DEBUG
   print("DEBUG flag set")
#else 
   print("No debug flag!")
#endif

This also works in Swift, but then we have to use OTHER_SWIFT_FLAGS and declare it like this:
-D DEBUG
and reading the variable value just like we did in the Objective C code above.

Many projects => xcconfig files

My app consists of an xcworkspace with a main project and several projects as dependencies. Let's call the projects my app is dependent on frameworks. Since I have several frameworks I do not want to have to setup build settings several times on.

Thus I am using xcconfig files. I have a main config, that is the config file for the main project, let's call it Main.xcconfig. I have another config file called Framework.xcconfig which starts with the line #include "Main.xcconfig", thus inheriting settings from Main. And of course I setup each framework to make use of said Framework.xcconfig file.

It is very convenient to declare the DEBUG flag when we have these config file, in Main.xcconfig we add:

OTHER_SWIFT_FLAGS[config=Debug] = -D DEBUG
GCC_PREPROCESSOR_DEFINITIONS[config=Debug] = DEBUG

And thus declaring the DEBUG flag for the configuration Debug for the main project and the framework projects (since Framework.xcconfig inherits from Main.xcconfig...).

Custom configurations

What if we want to be able to profile the app but with the DEBUG flag set? Profiling should be done with the same optimisation flags as Release. But we absolutely do not want to set the DEBUG flag for Release builds.

Why not create a new configuration, let's call it Profiling. Here comes the problem! Of course we should create this new configuration to the main project. Then we edit our scheme and for Profile and under Build Configuration we chose the new configuration Profiling.

Now we can set the DEBUG flag for Profiling in the Main.xcconfig file.

OTHER_SWIFT_FLAGS[config=Debug] = -D DEBUG // we keep this
GCC_PREPROCESSOR_DEFINITIONS[config=Debug] = DEBUG // we keep this
OTHER_SWIFT_FLAGS[config=Profiling] = -D DEBUG
GCC_PREPROCESSOR_DEFINITIONS[config=Profiling] = DEBUG

We try running the simulator and we see "No debug flag!", which is expected since for running we are using the Debug configuration and thus not declaring the DEBUG flag.

So we try profiling and start some Instruments measurement and open the console. There we see the message "DEBUG flag set"

It works, great!

Configurations are NOT inherited from parent project

We just checked against the DEBUG flag in the Main project. What happens if we in some of our frameworks want to check against our flags. So we try the #ifdef DEBUG in some framework. That works, since all frameworks have the configuration Debug, since it is default for all project (together with Release).

Then we try #ifdef DEBUG in one of our framework projects and start profiling using Instruments again. And now we see the message "No debug flag!"

Oh no! It is not working! Why not?! Well I do not know but the only reasonable conclusion must be that our projects added as dependencies - our frameworks - do not inherit the Profiling configuration from the Main project.

For me this is unbelievable... It feels like a flaw in Xcode.

Bad solution

I don't know of any other solution than adding the same configuration Profiling to all framework project (at least for frameworks where I know I want to check against that flag). But this feels like such an ugly solution!. I have at least 10 frameworks and it feels really ugly to have to add a certain configuration to each framework.

Alternative (terrible!) solution

Yes of course another solution would be to use the Release configuration for profiling, and declaring the DEBUG flag in Main.xcconfig like this:

OTHER_SWIFT_FLAGS[config=Release] = -D DEBUG
GCC_PREPROCESSOR_DEFINITIONS[config=Release] = DEBUG

But since we want to be able to check against DEBUG flag in framework we need to add the two lines above, declaring the flag, to Frameworks.xcconfig as well.

And of course use the Release configuration for profiling as Build Configuration for the scheme.

Then we can add a new configuration called AppStore to our Main project, and only the Main project and use that for archiving the app. So far so good?

IT'S A TRAP!

No this is not a good idea! Because I just said that the configurations are not inherited between project and parent project. Thus our frameworks will not inherit this new AppStore configuration, so when the frameworks get built/archived I have seen them "fallback" to the Release configuration (not sure if you can chose "default"/"fallback" configuration somewhere? Maybe it will fallback to the one you used as base for the new configuration?).

But since we just added declaration of the DEBUG flag for the configuration Release for the Main project and all our frameworks, and it is the Release configuration that is used for all our frameworks when we archive the app => our production app will include debug code!. This is highly unwanted and potentially dangerous.

Good solution?

I know of none... Do you? Wouldn't it be great if configurations would be inherited from parent project? That would solve everything! Apple... pretty please?

Vaientina answered 29/11, 2016 at 10:19 Comment(6)
You can inherit the configurations from the parent by using the special variable $(inherited). Have you tried this?Tellurion
@macmoonshine Are you not confusing things here? I believe you are think of inherited target settings, for a certain key, e.g. GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG. I am not using target settings directly. I am using xcconfig files and thus that would translate to GCC_PREPROCESSOR_DEFINITIONS[Profiling] = $(inherited) DEBUG. The problems is that the configuration Profiling is not available for our frameworks. It is a catch 22. It is the inheritance of configurations I am after! Not inheritance of a certain target build setting (like GCC_PREPROCESSOR_DEFINITIONS).Vaientina
<SOME_SETTING>[config=<MY_CONFIGURATION>] = <SOME_VALUE> in .xcconfig file OR equivalent <SOME_SETTING>= <SOME_VALUE> for the row MY_CONFIGURATION in Target Settings is not possible if MY_CONFIGURATION does not exist for said target (project). It is self explanatory for the Target Settings case (i.e. when not using .xcconfig files), because there is no row for MY_CONFIGURATION! And in the .xcconfig files the [config=<MY_CONFIGURATION>] just fails, since it does not exist. Once again, how can I make sure to inherit MY_CONFIGURATION from parent project?Vaientina
@Vaientina did you find a solution or a workaround since ?Miscellaneous
@Miscellaneous no unfortunately I did notVaientina
This is proof that Apple does not careVoltmeter
D
1

As workaround: Create xconfig files per project and global ones. In the project xconfig files include the global one via

#include "path/to/File.xcconfig"

Hope this help

Deflected answered 12/12, 2019 at 13:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.