How to append values in xcconfig variables?
Asked Answered
L

8

50

I'm using Xcode and .xcconfig files. I'm trying to append some values in the preprocessor definitions, but I simply can't make it work.

I tried the following (as well as many variations of this), but no luck so far:

GCC_PREPROCESSOR_DEFINITIONS = '$(GCC_PREPROCESSOR_DEFINITIONS) NEW_VALUE'

The NEW_VALUE symbol is simply never added to the preprocessor definitions.

Does anyone had success appending new values to variables in xcconfig files?

Lyre answered 8/9, 2009 at 13:19 Comment(0)
F
36

For reasons stated in other answers to this question, you can't inherit values easily.

I recommend defining your settings in cascade. Let's assume APP is your project prefix and make this simple defining only a few CFLAGS:

platform.xcconfig:

APP_PLATFORM_CFLAGS = -DMAS=1

project.xcconfig:

#include "platform.xcconfig"
APP_PROJECT_CFLAGS = -DBETA=1

target-one.xcconfig:

#include "project.xcconfig"
APP_TARGET_CFLAGS = -DSUPER_COOL=1
#include "merge.xcconfig"

target-two.xcconfig:

#include "project.xcconfig"
APP_TARGET_CFLAGS = -DULTRA_COOL=1
#include "merge.xcconfig"

merge.xcconfig:

OTHER_CFLAGS = $(inherited) $(APP_PLATFORM_CFLAGS) $(APP_PROJECT_CFLAGS) $(APP_TARGET_CFLAGS)

Then, you'll base each of your targets build configurations on target-xxx.xcconfig. A real project will use more complex setups, using a configuration file for the project and a different one for the target, but you get the idea.

Also, remember that $(inherited) refers to higher level in the hierarchy, not earlier. For instance, it inherits from Project level at Target level. Not sure if this apply to Xcode 4 too.

This is a simplification of GTM, go there to learn more.

Fantasize answered 13/3, 2011 at 12:39 Comment(0)
B
23

As stated in other answers, prior to Xcode 10, xcconfig files could not simply inherit and extend each other's values. But,

Since Xcode 10, xcconfig now work as one might expect them to : $(inherited) actually expand to the previously defined value of the variable.

When an .xcconfig file contains multiple assignments of the same build setting, later assignments using $(inherited) or $(<setting_name>) will inherit from earlier assignments in the .xcconfig. The legacy build system caused every use of $(inherited) or $(<setting_name>) to skip any other values defined within the .xcconfig. To detect whether your .xcconfig is affected by this improvement, running defaults write com.apple.dt.XCBuild EnableCompatibilityWarningsForXCBuildTransition -bool YES in Terminal will cause Xcode to generate a warning about this situation.

(Xcode 10 beta 1 release notes)

So for example, given two simple .xcconfig files:

// Generic.xcconfig
OTHER_SWIFT_FLAGS = $(inherited) -DMY_GENERIC_FLAG
// Debug.xcconfig
#include "Generic.xcconfig"
OTHER_SWIFT_FLAGS = $(inherited) -DMY_DEBUG_FLAG

Assuming your project uses Debug.xcconfig for its Debug configuration, you'll get the expected value -DMY_GENERIC_FLAG -DMY_DEBUG_FLAG for OTHER_SWIFT_FLAGS.

(instead of just -DMY_DEBUG_FLAG in Xcode 9 and earlier releases)


The new behavior is pretty straightforward: $(inherited) is simply replaced by the previously defined value of the variable, if any.

So in the previous example, if we expand the #include statement, we'll get the following xcconfig file:

// Merged xcconfig files after resolving #include
OTHER_SWIFT_FLAGS = -DMY_GENERIC_FLAG
OTHER_SWIFT_FLAGS = $(inherited) -DMY_DEBUG_FLAG
  • On the first line OTHER_SWIFT_FLAGS value is -DMY_GENERIC_FLAG ( $(inherited) expands to nothing, because this is the first definition of OTHER_SWIFT_FLAGS we encounter1).
  • On the second line OTHER_SWIFT_FLAGS if overwritten, and its value is now -DMY_GENERIC_FLAG -DMY_DEBUG_FLAG (its previous value + the newly added flag).

On a more complex xcconfig setup, things could look like this:

// First.xcconfig
OTHER_SWIFT_FLAGS = $(inherited) -DMY_FIRST_FLAG
// Second.xcconfig
OTHER_SWIFT_FLAGS = $(inherited) -DMY_SECOND_FLAG
// Last.xcconfig
#include "Generic.xcconfig"
OTHER_SWIFT_FLAGS = $(inherited) -DMY_LAST_FLAG
// Merge.xcconfig
#include "First.xcconfig"
#include "Second.xcconfig"
OTHER_SWIFT_FLAGS = $(inherited) -DMY_INTERMEDIATE_FLAG
#include "Last.xcconfig"

We'll assume this time we're using the Merge.xcconfig in our configuration.

The resolved value for OTHER_SWIFT_FLAGS will then be -DMY_FIRST_FLAG -DMY_SECOND_FLAG -DMY_INTERMEDIATE_FLAG -DMY_GENERIC_FLAG -DMY_LAST_FLAG.

This might be surprising at first, but it actually makes sense: once the #include are resolved, we end up with this xcconfig:

OTHER_SWIFT_FLAGS = $(inherited) -DMY_FIRST_FLAG
OTHER_SWIFT_FLAGS = $(inherited) -DMY_SECOND_FLAG
OTHER_SWIFT_FLAGS = $(inherited) -DMY_INTERMEDIATE_FLAG
OTHER_SWIFT_FLAGS = $(inherited) -DMY_GENERIC_FLAG
OTHER_SWIFT_FLAGS = $(inherited) -DMY_LAST_FLAG

The final resolved value is then the one defined on the last line, which is -DMY_LAST_FLAG plus the value it inherited from the previous line -DDMY_GENERIC_FLAG (which comes from Generic.xcconfig, included in Last.xcconfig) etc etc.

Note that naturally, if you forget $(inherited) in one of the definitions, you'll break the inheritance chain and only get the values from the bottom definitions, up to the definition without $(inherited).


1 One may expect the xcconfig file to inherit previous values defined at the Project level, but it doesn't seem to be the case


📡 As of Xcode 10 beta 1, it seems the build settings editor GUI doesn't properly resolve the correct value for variables defined in the xcconfig files, and displays the values as if resolved with the old pre-Xcode 10 behavior. I filed rdar://40873121 regarding this (https://openradar.appspot.com/radar?id=4925869923500032).

Bobbibobbie answered 6/6, 2018 at 23:34 Comment(9)
And xcodebuild -showBuildSettings has the bug too. However the macro definitions seem to work when preprocessing.Terrijo
The behavior described in this answer doesn't seem to be the case anymore as of Xcode 12.5 (the only Xcode version I tested). Is this the case for anyone else?Canoe
@RogerOba I just verified, and nothing seems to have changed in Xcode 12.5Bobbibobbie
@GuillaumeAlgis my testing scenario is exactly the same as the "generic + debug" one at the top of your answer. What I observe is that the variable (in my case, PRODUCT_NAME) is empty when I use $(inherited) (yeah I'm #include'ing the generic.xcconfig reference at the top), but if I define a custom variable (MY_VARIABLE) in the generic.xcconfig, the $(inherited) does return its value correctly. Are you seeing something different? Could you be so kind to try with PRODUCT_NAME?Canoe
And FWIW I was able to workaround my issue by changing the project's configuration xcconfig file to generic.xcconfig and all its child targets use the debug.xcconfig (in which I removed the #include of the generic.xcconfig) - this way the $(inherited) works. Previously, I was assigning debug.xcconfig to the parent and all children targets, and #including. Maybe it's worth clarifying in the answer what configuration setup you're using @GuillaumeAlgis ? This workaround of mine works with a single-level inheritance, but not multiple (like your "1st, 2nd intermed and last" example).Canoe
@RogerOba using $(inherited) in PRODUCT_NAME seems indeed to be problematic. I think its related to the bug I mentioned at the end of my answer. In some places Xcode understands the new inheritance system (I have a .app created with the full inherited name), and in some other places it doesn't, which causes problems. I reproduced in a sample project : github.com/guillaumealgis/…Bobbibobbie
Thanks for confirming @GuillaumeAlgis ! I'll move on with my workaround for now. Really appreciate your answer!Canoe
@RogerOba the way you inherit the variables from the project's xcconfig is interesting, I always only used at most one xcconfig file per {Target ; Build Configuration} combination, so I never played with that. It seems to limit the inheritance chain to two members only though (one parent + one child).Bobbibobbie
@GuillaumeAlgis Thanks, for you info! It was very subtle (both in the answer and in the radar), that its only a GUI problem but that actual flags used during the build are actually correct. I tested using OTHER_SWIFT_FLAGS in Xcode Version 14.0 beta 5 (14A5294e) and observed incorrect values in the GUI but correct values in the build output. May help others to make it more explicit in the answer. Also, as your important info is at the bottom of a very long answer, since its very important, it might be useful to make it more prominent in the answer.Constanceconstancia
L
10

According to the Xcode Build System Guide:

When a configuration unit contains more than one definition for a particular build setting, Xcode uses the last definition in the unit. Keep in mind that configuration files do not have access to build setting definitions made in configuration files they include. That is, you cannot modify the definition made in an included configuration file; you can only replace it.

So, I guess this mean that it is not possible to append values to a given variable.

Lyre answered 9/9, 2009 at 19:44 Comment(0)
I
9

This works:

xcodebuild GCC_PREPROCESSOR_DEFINITIONS='$(value) NEW_VALUE'
Install answered 27/9, 2012 at 16:30 Comment(1)
Thanks, I simply use GCC_PREPROCESSOR_DEFINITIONS=$(value) NEW_VALUE though (no quotes).Imagism
S
4

There is another question with an answer that might help with this particular problem. It describes a technique that has each layer compose a subset of the definitions, then bring them all together at the leaf-level xcconfig.

Smithson answered 15/1, 2010 at 19:37 Comment(0)
B
4

I think I've stumbled on a slightly better approach while trying to integrate the Cocoapods xcconfig files into my own. I like to set the following in my projects

GCC_PREPROCESSOR_DEFINITIONS = CONFIGURATION_$(CONFIGURATION)

Unfortunately this conflicts with the definitions that comes with the Pods.xcconfig. As is stated elsewhere $(inherited) doesn't work as expected. What does work is the following

GCC_PREPROCESSOR_DEFINITIONS[config=*] = CONFIGURATION_$(CONFIGURATION) $(inherited)

UPDATE:

If you need to override a setting for a particular configuration then you'd be tempted to write something like

GCC_PREPROCESSOR_DEFINITIONS[config=*] = CONFIGURATION_$(CONFIGURATION) $(inherited)
GCC_PREPROCESSOR_DEFINITIONS[config=Debug] = DEBUG=1 CONFIGURATION_$(CONFIGURATION) $(inherited)

Sadly this won't work BUT putting the second declaration into a file that only get's loaded by the Debug configuration will properly override setting.

Bignoniaceous answered 14/8, 2014 at 22:13 Comment(1)
The GCC_PREPROCESSOR_DEFINITIONS[config=*] = ... syntax works in Xcode 5. I used it in target-specific .xcconfig files to append centrally defined project-wide build settings to target-specific settings: BLA[config=*] = $(BLA_TARGET_SPECIFIC) $(BLA_PROJECT_WIDE).Adorno
T
1

This works for me in Xcode 2.4.1:

GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS) NEW_VALUE"

You do have to sometimes allow a few seconds between editing a config file and the change showing up in a target's Build Info.

Tahr answered 9/9, 2009 at 10:11 Comment(1)
Not much luck with XCode 3.2 :(Lyre
C
0

You want to use the placeholder $(inherited) to represent the value inherited from lower levels, e.g.

GCC_PREPROCESSOR_DEFINITIONS = "$(inherited) NEW_VALUE"
Cracow answered 15/1, 2010 at 21:51 Comment(2)
This looks like a perfect answer, but it’s wrong. $(inherited) doesn’t work within xcconfig files; in particular, you can’t use it to extend a definition from an included file.Mimesis
then how do you extend?Sparteine

© 2022 - 2024 — McMap. All rights reserved.