Simon
Simon

Reputation: 26013

How can I dynamically set my app's build number without dirtying my source tree?

I'm using git-svn and I'm trying to embed my revision number into my iOS app. At the moment, I have a build phase which runs the following script:

SVN_REVISION=$(git svn find-rev HEAD)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $SVN_REVISION" "$INFOPLIST_FILE"

The problem with this is that, since the repo always contains the previous revision, the script always makes my Info.plist dirty.

Is it possible for me to dynamically set my app's build number without dirtying my source tree?

Upvotes: 5

Views: 1707

Answers (2)

trickster
trickster

Reputation: 71

Mecki thank you for the excellent answer! I applied the same concept to set a version timestamp and the current git SHA for the build. FYI I just ran into a small issue. It seems that, at least in Xcode 5, if you specify an output file the script step uses it as a cache, so no matter the changes I made to my actual app code the script reported that it had ran but the values were not the current ones... I had to move the output file declaration to the script itself to solve the issue, i.e. added

SCRIPT_OUTPUT_FILE_0="$CONFIGURATION_TEMP_DIR/InfoPlist.pch"

to the top of my script. Additionally the original plist should also be touched in order for the build step to copy the new values in, so I also added

`touch $SCRIPT_INPUT_FILE_0`

after the previous output file declaration. This touch operation does not make git detect the change as commit-able.

Cheers

Upvotes: 2

Mecki
Mecki

Reputation: 133219

1) Add a new target to your project of type "Aggregate", e.g. you may name it "Update Info.plist Prefix Header"; just use that as "Product Name" in the dialog.

2) Add a Run Script build phase to this new target with the following source code:

#!/bin/sh
SVN_REVISION=$(git svn find-rev HEAD)
echo "#define SVN_REVISION $SVN_REVISION" > "$SCRIPT_OUTPUT_FILE_0"


3) Add an output file to your script, name it

$(CONFIGURATION_TEMP_DIR)/InfoPlist.pch


4) Open the Build Phases of your iOS app.

5) Add the aggregate target you created before as dependent target (add it to "Target Dependencies"). This means Xcode will always first build this target before it will build your iOS target.

6) Open the Build Settings of your iOS app.

7) Search for the setting "Info.plist Preprocessor Prefix File" and change it to exactly the same value you used for the output file in step (3).

8) Search for the setting "Preprocess Info.plist File" and make sure it is enabled.

9) Open your current Info.plist file and change the value of CFBundleVersion to SVN_REVISION. Do not use $(SVN_REVISION) or ${SVN_REVISION}; this is no build setting or environment variable replacement, this is a preprocessor replacement, so just use SVN_REVISION.


That's it. Each time you build your iOS app, Xcode first builds the aggregate target, which updates the PCH file, and when it builds your iOS app, it will run the Info.plist file through the C pre-processor (using the PCH file as prefix header) before copying it to your application. The pre-processor will replace SVN_REVISION since it is defined as a macro in your PCH file.

Important Notes

Some people may think it is a better idea to use $(DERIVED_FILE_DIR) instead of $(CONFIGURATION_TEMP_DIR). Well, in theory they are right, yet there is just one problem in practice: The derived file dir is different for every target, while the configuration temp dir is the same (it is only different for every build configuration). When using derived file dir, the PCH file is written to the derived file dir of the aggregate target, yet when building the iOS app, Xcode will search for this file in the derived file dir of the iOS app and thus it won't find the file.

Some people may also think it is a better idea to just add the Run Script phase that updates the prefix header as the first build phase of you iOS app instead of creating a separated target for it (this would also resolve the derived file dir issue mentioned above). Again, nice idea in theory but cannot work in practice: If preprocessing is requested, the Info.plist is preprocessed before the first script phase is even executed, so if the PCH file does not exist already or has not been updated already, either the build terminates with an error or an outdated SVN revision is written to the plist file. That's why you need a separate target for this task that is guaranteed to be build before your actual target is.

Upvotes: 10

Related Questions