How can I conditionally include a file based on build configuration in Xcode?
Asked Answered
M

6

87

I have an Xcode project with a large number of targets where I would like to include a settings bundle for apps built under the Ad-hoc and Debug configurations, but not under the Release configuration.

Build Phases don't seem to allow for making themselves conditional on configuration (they can obviously be conditional on target, but doubling the number of targets in the project would make it completely unusable).

That leaves writing a custom Build Rule. My plan is to exclude the Settings.bundle from all targets, and create a build rule that conditionally copies it into the product package, but applicable examples are really hard to find.

The build rule I've started has the Process setting set to "Source files with names matching:" and Settings.bundle as the name. The Using setting is "Custom script:".

My custom script is as follows (with the caveat that my bash scripting is on a cargo cult level):

if [${CONFIGURATION} = 'Debug'] then
    cp -r ${INPUT_FILE_PATH} ${DERIVED_FILES_DIR}/.
fi

Finally, I have ${DERIVED_FILES_DIR}/Settings.bundle listed as an output file.

Since I'm here, it should be obvious that it's not working. My first question is whether there is somewhere I can view the output of the build rules as the execute to make sure that 1) it's actually being executed and that 2) I don't have a stupid syntax error somewhere.

Also, what's the proper location (in the form of an environment variable) to copy the output to?

Marcela answered 13/12, 2011 at 21:34 Comment(0)
M
95

I finally figured it out.

For each target for which you want to conditionally include the settings bundle, choose its Project from the source list, choose the target, and switch to the "Build Phases" tab.

Click the "Add Build Phase" button and choose "Add Run Script".

Then enter the following for the script:

if [ "${CONFIGURATION}" == "Debug" ]; then
    cp -r "${PROJECT_DIR}/Settings.bundle" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app"
fi
Marcela answered 9/2, 2012 at 23:7 Comment(1)
Thanks, I have used to approach to manage firebase project plist files for different configurations.Cornflower
T
53

I know this question has been answered already, and the answer was very helpful to me, but I wanted to throw my own modified solution out there as well.

My requirement was to have different settings bundles for different build configurations, rather than just not including it at release. Assuming a simplistic approach of only Debug and Release configurations, here's how to do it:

Start by adding 2 settings bundles to the project, named Settings-debug.bundle and Settings-release.bundle and then remove these files from the Copy Bundle Resources build phase. Next add a user defined build setting called SETTINGS_BUNDLE, which has different values for each configuration:

Debug        ${PROJECT_DIR}/relative/path/to/Settings-debug.bundle
Release      ${PROJECT_DIR}/relative/path/to/Settings-release.bundle

Next add a run-script build phase (after Copy Bundle Resources) named Copy Settings Bundle with a modified version of the script in Frank's solution.

cp -r "${SETTINGS_BUNDLE}/" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Settings.bundle"

The difference here is that the copied bundle is always named Settings.bundle regardless of the source name.

You then need to add another build phase script to prevent code signing errors when the only changes are in the settings bundles. It forces the code signing step to occur on every build. This should run before the Compile Source Files build phase. I called mine Force Codesign.

touch "${PROJECT_DIR}/relative/path/to/main.m"
Thirddegree answered 10/10, 2013 at 14:6 Comment(4)
Great solution for a debug-only application preference pane in the built-in settings app, which is what I was after. Thank you very much!Bree
You can also write: cp -r "${PROJECT_DIR}/settings_bundle/Settings-${CONFIGURATION}.bundle/" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Settings.bundle" This will let you avoid adding any user defined build entry and will use the files 'settings_bundle/Settings-Debug.bundle', 'settings_bundle/Settings-Release.bundle', etcStasiastasis
As SETTINGS_BUNDLE variables, I used ${PROJECT_DIR}/${PROJECT_NAME}/Resources/Settings.bundleNoria
And instead of looking for the files in the Copy Bundle Resources build phase to have them removed, simply remove any target membership for the bundle files in the File inspector. Quicker.Noria
V
32

For complied sources, there is a poorly documented user defined build setting that can be added. Files can be both excluded and included from compilation

Go to your target's Build Settings > Tap the + button > Add User-Defined Setting

The key is either INCLUDED_SOURCE_FILE_NAMES or EXCLUDED_SOURCE_FILE_NAMES

The value is a space separated list of file paths

See reference: http://lists.apple.com/archives/xcode-users/2009/Jun/msg00153.html

Viccora answered 7/4, 2016 at 16:40 Comment(1)
I can confirm that adding EXCLUDED_SOURCE_FILE_NAMES is still working (and still undocumented) in Xcode 8.2..xWilber
D
11

(Tested with Xcode 9.3)

I can't find when Xcode included this feature but EXCLUDED_SOURCE_FILE_NAMES is now directly available in Build Settings > Build Options > Excluded Source File Names.

So you no longer need to create a User-Defined Setting.

See below: enter image description here

It will automatically add this line in your .pbxproj. enter image description here

Dentist answered 24/4, 2018 at 11:45 Comment(2)
Is there a reason to use explicit exclusion with EXCLUDED_SOURCE_FILE_NAMES instead of explicit inclusion via INCLUDED_SOURCE_FILE_NAMES? When I use inclusion, it straight up doesn't work. Tried with a raw json file and an xcassets fileSoilure
Apple says This setting [INCLUDED_SOURCE_FILE_NAMES] is only useful when combined with EXCLUDED_SOURCE_FILE_NAMES Is what you say related to this ? Source: developer.apple.com/documentation/xcode/…Dentist
C
7

Settings.bundle is always copied into destination area no matter whether Release or Debug configuration. So, maybe you need the following code:

if [ ${CONFIGURATION} == "Release" ]; then
    rm -rf ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Settings.bundle
fi
Cellulous answered 30/7, 2014 at 3:47 Comment(0)
P
3

I am no shell script expert but I think you need space between the square brackets and the condition. Also, quoting the variables may help:

if [ "${CONFIGURATION}" = "Debug" ] then
    cp -r "${INPUT_FILE_PATH}" "${DERIVED_FILES_DIR}"/.
fi

As for the location, I use "$BUILT_PRODUCTS_DIR"/"$FULL_PRODUCT_NAME" for the root of my OS X app bundle.

Perineurium answered 13/12, 2011 at 22:55 Comment(2)
This gives me syntax errors when I build. Frank's solution, copied and pasted, worked perfectly.Postglacial
To be wholly fair, this guy's answer was posted three months earlier.Vallonia

© 2022 - 2024 — McMap. All rights reserved.