How to automatically set the version and build num

2019-03-10 20:19发布

The version and build number (or version and short version) of a Watchkit app and extension have to be set to the same value as the containing app.

I use environment variables to set the apps version in the Info.plist dynamically at build time. That also works fine for the Watchkit extension, but not for the Watchkit app.

The environment variables I use have to be provided in the plist for the main app and extension without ${} (for variable ${VERSION} I set VERSION).
if I do the same for the Watchkit app, it is taking the string itself, not the value. If I provide it with dollar & brackets there is no data in the variable.

Any idea how to set the variables for the Watchkit app?

8条回答
兄弟一词,经得起流年.
2楼-- · 2019-03-10 20:50

stk's answer is right, but I wanted to add my findings as well.

One way to solve the problem is by using agvtools:

Create a new target in OSX > Other > External Build Sytem Add a run script similar to this one:

#!/bin/bash

#read vesion number from version.txt in project root
VERSION=$(head -n 1 version.txt)
BUILD=`git rev-list $(git rev-parse --abbrev-ref HEAD) | wc -l | awk '{ print $1 }'`

echo "${VERSION} (${BUILD})"

agvtool new-marketing-version ${VERSION}
agvtool new-version -all ${BUILD}

exit 0

I have a version.txt file with only my version number in it (marketing version or short bundle version) that can be easily adjusted by any CI system and use the number of my git SHAs as build number (bundle version) Adjust the sources for VERSION and BUILD to fit your requirements

Run the scheme that has been created for the new target before your build/archiving.

In case you need to have this as a dependency for your main target - this will fail, as it will stop the execution of the following targets (if somebody knows how to prevent that, I would be thankful for a hint)

But you can still achieve that with a script like the following executed for each of your plists (similar to what stk provided):

#!/bin/sh
#
# usage:
# set-version-in-plist.sh LIST VERSION BUILD
# LIST:      Info.plist path & name
# VERSION:   version number xxx.xxx.xxx
# BUILD:     build number xxxxx
#

# Location of PlistBuddy
PLISTBUDDY="/usr/libexec/PlistBuddy"

${PLISTBUDDY} -c "Set :CFBundleShortVersionString $2" "$1";
${PLISTBUDDY} -c "Set :CFBundleVersion $3" "$1";

Save this script as a file, make it executable (chmod +x SCRIPTNAME) Then execute it with the mentioned parameter for all your plists

This solution is not so convenient as the agvtools solution, but it should not stop your build when used in a dependency ...

查看更多
贼婆χ
3楼-- · 2019-03-10 20:52

I use this to update all the targets:

#!/bin/bash
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
buildNumber=$(($buildNumber + 1))
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$SRCROOT/Great WatchKit App/Info.plist"
查看更多
成全新的幸福
4楼-- · 2019-03-10 20:57

If it would be useful to supplement other answers with my own personal experience. These centred around build failure caused by ValidateEmbeddedBinary.

ValidateEmbeddedBinary will fail if the CFBundleVersion is not the same in the embedded WatchKit app and the parent app.

The error looks something like:

(null): error: The value of CFBundleVersion in your WatchKit app's Info.plist (1234) does not match the value in your companion app's Info.plist (7931). These values are required to match.

Working in XCode 7.3, the following will first update the parent app's plist. Then it updates the Debug or Release WatchKit app before PBXCp executes to copy it to the parent app directory:

#!/bin/sh

git=`sh /etc/profile; which git`
appBuild=`"$git" rev-list HEAD --count`

appPlistPath="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
watchKitPlistPath="${BUILT_PRODUCTS_DIR}/../${CONFIGURATION}-watchos/${PRODUCT_NAME} WatchKit App.app/Info.plist"

echo "Setting App CFBundleVersion $appBuild at info plist path at ${appPlistPath}"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $appBuild" "${appPlistPath}"

echo "Setting WatchKit App CFBundleVersion $appBuild at ${watchKitPlistPath}"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $appBuild" "${watchKitPlistPath}"

The above uses git's commit count as CFBundleVersion.

查看更多
三岁会撩人
5楼-- · 2019-03-10 20:57

I have a Run Script that I attach to my main app target. It will propagate the WatchKit Extension and the WatchKit app upon building the app.

It is completely reusable. Enjoy!

buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/${INFOPLIST_FILE}")

buildNumberDec=$(($buildNumber + 1))

/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumberDec" "${PROJECT_DIR}/${INFOPLIST_FILE}"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumberDec" "$SRCROOT/${PRODUCT_NAME} WatchKit Extension/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumberDec" "$SRCROOT/${PRODUCT_NAME} WatchKit App/Info.plist"
查看更多
相关推荐>>
6楼-- · 2019-03-10 21:00

My CFBundleVersion is the number of commits on my master branch on the git repo.

On my main app target, in Build Phases > + New Run Script Phase I've added this script:

# Set the build number to the count of Git commits
buildNumber=$(git rev-list --count HEAD)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "${PROJECT_DIR}/${INFOPLIST_FILE}"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$SRCROOT/app WatchKit Extension/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$SRCROOT/app WatchKit App/Info.plist"

From app WatchKit App the app should be the name of your app, but check the exact path.

查看更多
叛逆
7楼-- · 2019-03-10 21:08

To extend this thread, if someone encounters similar problem as me, hopefully script below will help.

For instance, I have watch target, watch extension, app share extension in my project.

I used run phase to update project's plist as suggested, and it works if I build the project, the .plist files are all updated as expected.

However the problem is when you archiving app(let's say all targets have different build number), the info plist in the archived projects was not updated. After a few times of try out, I found extension's plist files were copied before this run phase, and then the run phase script(updating project's plist) won't help with the archived plist. So I eventually changed the script to update compiled target's plist, and it works as I expected, I have same build number for all the targets in the application. Here is how I did it: add this script to each target's build phase:

infoPlistPath="${TARGET_BUILD_DIR}/${EXECUTABLE_FOLDER_PATH}/Info.plist"
PLISTBUDDY="/usr/libexec/PlistBuddy"
buildNumber=$(git rev-list HEAD | wc -l | tr -d ' ')
$PLISTBUDDY -c "Set :CFBundleVersion $buildNumber" "${infoPlistPath}"

For different target, this EXECUTABLE_FOLDER_PATH was different, and it will update the compiled target's info plist, instead of the project's info plist. Just a note I checked "Run script only when installing" as well since I only need this to be run for archiving

查看更多
登录 后发表回答