🗺 Command Line Flags Mapping

As we already know, a lot of build setting values map to different command line flags.

The mapping information is defined in xcspecs using one of the following keys:

CommandLineArgs

CommandLineFlag

CommandLinePrefixFlag

AdditionalLinkerArgs

ℹ️ Command Line Arguments

CommandLineArgs key-value entry is used to map build setting value to a list of command line arguments.

Scalar Types

A good example is the SWIFT_MODULE_NAME build setting of String type:

The $(value) is resolved to the current build setting value, e.g. the MyModule value is mapped to the -module-name "MyModule" Swift compiler flag.

List Types

For list types, each build setting value in the list is mapped to one or more command line flags. For example:

If the value of CLANG_ANALYZER_OTHER_CHECKERS is "checker1" "checker2" , it will be mapped to the following:

Enumeration Type

For enumeration types, mapping is defined for each enumeration case. For example:

ansi value is mapped to the -ansi compiler flag

value is mapped to the compiler flag compiler-default maps to no flags

maps to no flags All other enumeration values map to the -std=$(value) compiler flag.

Note the use of "<<otherwise>>" —this is similar to default: enum switch in C-like languages.

Boolean Type

Boolean type is mapped just like enumerations with two YES and NO cases.

ℹ️ Command Line Flag

CommandLineFlag is used to prepend the command line flag to the value of the build setting.

For example, SDKROOT is defined like so:

So if the value of SDKROOT is iphoneos , the corresponding Clang compiler flag will be -isysroot iphoneos .

In a way, CommandLineFlag is shorthand for using CommandLineArgs . I.e. in the SDKROOT example, CommandLineFlag = "-isysroot"; could be replaced with CommandLineArgs = ("-isysroot", "$(value)"); .

Handling of list, Enumeration , and Boolean types is similar to handling CommandLineArgs , too.

For example, given the definition:

The value like SYSTEM_FRAMEWORK_SEARCH_PATHS = "A" "B" "C" will be mapped to:

Enumerations deserve a special mention, because build flag mapping can be defined next to the value. A good example is MACH_O_TYPE :

ℹ️ Command Line Prefix Flag

CommandLinePrefixFlag maps build setting value to itself prefixed with a build flag.

It works just like CommandLineFlag . The only difference is that there’s no space between the build flag and the build settings value.

Good examples include LIBRARY_SEARCH_PATHS and FRAMEWORK_SEARCH_PATHS build settings of list type, where each list entry is mapped to -L"$(value)" or -F"$(value)" , respectively.

Another similar build setting often used by developers is OTHER_LDFLAGS , aka Other Linker Flags.

Finally, whenever you see a flag like -fmessage-length=0 it’s most likely mapped using the prefix flag rule.

ℹ️ Additional Linker Flags

Certain Swift or Clang compiler build settings map not only to compiler flags, but to linker flags as well.

Those build settings have an additional AdditionalLinkerArgs key-value pair. For example:

In this example, the -fobjc-arc flag will be added both to the Clang compiler and linker invocations.

The way mapping works for different build setting types is identical to the handling of CommandLineArgs .

References

As I’ve mentioned earlier, build settings from different xcspecs can reference each other.

Some build settings have a DefaultValue property, which can reference other build settings, e.g. DefaultValue = “$(BITCODE_GENERATION_MODE)"; . The default value of some build settings is defined by referencing other build settings:

The references can be nested as well. For example:

Here $(DEPLOYMENT_TARGET_SETTING_NAME) will be first resolved into a value like SOME_SETTING_NAME , and then $(SOME_SETTING_NAME) is resolved once again to a final value. This is similar to how build settings are resolved in xcconfigs.

Build settings reference can be used inside CommandLineArgs and all other mapping key-value entries.

Conditions

Conditions are defined using Condition key-value pair and are used to control whether or not a certain build setting is enabled.

For example, the SWIFT_BITCODE_GENERATION_MODE build setting only makes sense when ENABLE_BITCODE is set to YES :

Conditions have a C-like syntax, although string values don’t have to be quoted. Boolean operators such as ! , && , || , == , and != can be used:

Other Properties

There are other properties, such as:

Architectures — Defines which target architectures the build setting is applicable for.

— Defines which target architectures the build setting is applicable for. AppearsAfter — Controls the order in which 2 specific build settings appear in Xcode UI.

— Controls the order in which 2 specific build settings appear in Xcode UI. FileTypes — List of applicable file types.

— List of applicable file types. ConditionFlavors — Must have something to do with the way conditions are checked.

— Must have something to do with the way conditions are checked. There are a few other flags I didn’t have time to fully investigate yet.

👩‍🏫 Example

Let’s have a look at one build setting example and see how we can apply all the knowledge from this article to figure out which flags it will map to.

The build setting is CLANG_ENABLE_CODE_COVERAGE , and it enables one very important feature — code coverage.

CLANG_ENABLE_CODE_COVERAGE is defined in Clang LLVM 1.0.xcspec but strangely maps to no flags at all…

However, CLANG_ENABLE_CODE_COVERAGE is referenced by CLANG_COVERAGE_MAPPING .

So now we know the Clang compiler flags are added to the compiler invocation when code coverage is enabled.

Additionally, there’s another build setting that controls the linker flag:

It also comes with a very detailed comment.

Finally, CLANG_COVERAGE_MAPPING is also defined in Swift.xcspec:

Now all the pieces of the puzzle come together and it’s clear how Xcode does the mapping.

The only problem is that both CLANG_COVERAGE_MAPPING and CLANG_COVERAGE_MAPPING_LINKER_ARGS are hidden and don’t show up in Xcode UI. So how do those get set if they don’t reference CLANG_ENABLE_CODE_COVERAGE in their default value?

The code coverage can be enabled by editing the scheme in Xcode UI or by passing -enableCodeCoverage YES to xcodebuild invocation.

Xcode will then set both CLANG_COVERAGE_MAPPING and CLANG_COVERAGE_MAPPING_LINKER_ARGS to YES under the hood.

👷‍ Application

OK then, so it’s more or less clear how build settings are resolved, but what’s the practical application?

Well, there’s the purely academic application—you get to know how things work, which occasionally will come handy when you have a hard time figuring out various build settings messes.

While the official Xcode Build Settings reference page is a good resource to use, the extended Xcode Build Settings reference like this one includes information about compiler and linker flags, build settings cross-reference, and more.

But there are other ways to use this knowledge.

Let’s say you want to try out an alternative build system like Buck or Bazel. Those build systems are gaining popularity these days. Buck was created by Facebook while Bazel came from Google. Other companies such as Uber, AirBnB, and Dropbox use Buck; while Lyft is using Bazel to build their mobile apps.

Let’s further assume that over the years you’ve created and maintained a number of amazing xcconfig files. Those xcconfigs have all the compiler and linker build settings fine-tuned for your use. When moving to tools like Buck, you can’t just bring xcconfigs over. Instead you’d need to translate xcconfigs to compiler and linker flags and then use those with Buck.

Well, now you have all the knowledge to do so.

You’d need to start with reading and resolving xcconfigs and then map resolved build settings to build flags. It’s not a straightforward task and may take a while to implement. Luckily for you, there’s a Fastlane plugin that does just that.

Like many unofficial tools, this plugin is reverse engineering the ways Xcode works, so use it at your own risk.