Better way of incrementing build number?

2019-01-01 11:34发布

I have been using a shell script as part of my Xcode build process to increment the build number within the plist file, however it's making Xcode 4.2.1 crash frequently (with an error about the target not belonging to a project; I'm guessing the changing of the plist file is confusing Xcode in some way).

The shell script did this so that the build number is only incremented by agvtool when a file is newer than the plist file (so just building didn't increment the value):

if [ -n \"`find ProjDir -newer ProjDir/Project-Info.plist`\" ]; then agvtool -noscm next-version -all; else echo \"Version not incremented\"; fi

Is there a way to increment the build number (in the plist file, or anywhere else) that doesn't break Xcode?

EDIT: Here is my final solution, based on the suggestion of @Monolo. I created the following script in ${PROJECT_DIR}/tools (sibling to the .xcodeproj directory):

#!/bin/sh

if [ $# -ne 1 ]; then
    echo usage: $0 plist-file
    exit 1
fi

plist="$1"
dir="$(dirname "$plist")"

# Only increment the build number if source files have changed
if [ -n "$(find "$dir" \! -path "*xcuserdata*" \! -path "*.git" -newer "$plist")" ]; then
    buildnum=$(/usr/libexec/Plistbuddy -c "Print CFBundleVersion" "$plist")
    if [ -z "$buildnum" ]; then
        echo "No build number in $plist"
        exit 2
    fi
    buildnum=$(expr $buildnum + 1)
    /usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildnum" "$plist"
    echo "Incremented build number to $buildnum"
else
    echo "Not incrementing build number as source files have not changed"
fi

EDIT 2: I have modified the script to incorporate @Milliways suggestion.

I then invoked the script from Xcode target 'Build Phases' section: Xcode build phases screenshot

EDIT 3: As per @massimobio's answer, you'll need to add quotes around the plist argument if it contains spaces.

EDIT 4: Just to update that my preferred method of invoking this build script is now to create a separate target and make the app target dependant upon this Bump Build Number target. This ensures that it is invoked before the app target does anything with the plist (I've noticed that it likes to process the plist at the start of the build). I've also switched to a purely python-based solution that keeps the version number in a separate file, and writes version source files, as this is more useful for cross-platform products (i.e. Visual Studio under Windows can invoke the script, and obviously cmake/make-type builds can do so also). This has the benefit that the build number is always the same even under different platforms, and it's also possible to update the Visual Studio Resource.rc file with the current version/build as well.

Here is the python script I currently use to update Info.plist files within Xcode project.

20条回答
春风洒进眼中
2楼-- · 2019-01-01 12:15

Thanks for the script. It works great.

My Info.plist is in a subdirectory with a name containing spaces so I had to modify the Run Script with quotes around the plist path:

${PROJECT_DIR}/tools/bump_build_number.sh "${PROJECT_DIR}/${INFOPLIST_FILE}"

and the shell script in the same way with quotes around all the paths:

#!/bin/sh

if [ $# -ne 1 ]; then
    echo usage: $0 plist-file
    exit 1
fi

plist=$1
dir=$(dirname "$plist")

# Only increment the build number if source files have changed
if [ -n "$(find "$dir" \! -path "*xcuserdata*" \! -path "*.git" -newer "$plist")" ]; then
    buildnum=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$plist")
    if [ -z "$buildnum" ]; then
        echo "No build number in $plist"
        exit 2
    fi
    buildnum=$(expr $buildnum + 1)
    /usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildnum" "$plist"
    echo "Incremented build number to $buildnum"
else
    echo "Not incrementing build number as source files have not changed"
fi
查看更多
十年一品温如言
3楼-- · 2019-01-01 12:16

If I understand your question correctly, you want to modify the Project-Info.plist file, which is a part of the standard project template of Xcode?

The reason I ask this is that Project-Info.plist normally is under version control, and modifying it means that it will be marked as, well, modified.

If that is fine with you, then the following snippet will update the build number and mark the file as modified in the process, where get_build_number is some script (i.e., a placeholder in this example) to get the (possibly incremented) build number that you want to use:

#!/bin/sh

# get_build_number is a placeholder for your script to get the latest build number
build_number = `get_build_number`

/usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${build_number}" ProjDir/Project-Info.plist

PlistBuddy allows you to set any key in a plist file, not just the version number. You can create all the plist files you want, and include them in the resources if needed. They can then be read in from the bundle.

As to your need to show the version in the about pane and other places, you can also look into setting CFBundleGetInfoString and CFBundleShortVersionString.

查看更多
几人难应
4楼-- · 2019-01-01 12:16

I've messed around with a lot of the answers on this question, and none of them quite satisfied me. However, I finally came up with a mixture that I really like!

There are two steps, one at the beginning and one at the end of your build phases.

At the beginning:

# Set the build number to the count of Git commits
buildNumber=$(git rev-list HEAD | wc -l | tr -d ' ')
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "${PROJECT_DIR}/${INFOPLIST_FILE}"

At the end:

# Set the build number to "DEVELOPMENT"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion DEVELOPMENT" "${PROJECT_DIR}/${INFOPLIST_FILE}"

Looking at the Info.plist in Xcode you'll see the version number is "DEVELOPMENT", but the built app will have a constantly increasing build number. (As long as you always do your builds off the same branch.)

Setting the version number back to a constant string at the end prevents the Info.plist file from being changed by building the app.

Why I like this method:

  • Easy
  • Doesn't pollute Git version history
  • CFBundleVersion is totally automatic
  • The pretty version number can be modified whenever I want
查看更多
泛滥B
5楼-- · 2019-01-01 12:16

This whole entry was extremely helpful. I used this trick but set up my script as a post-commit hook in GIT, so CFBundleVersion is incremented after every successful commit. The hook script goes in .git/hooks. A log is left in the project directory.

This meets my most basic criterion. I want to be able to pull a version from GIT and rebuild the exact build I had previously. Any increment done during the build process does not do this.

Here is my script:

#!/bin/sh
#
# post-commit
#
# This script increments the CFBundleVersion for each successful commit
#

plist="./XYZZY/XYZZY-Info.plist"
buildnum=$(/usr/libexec/Plistbuddy -c "Print CFBundleVersion" "$plist")
if [ -z "$buildnum" ]; then
    exit 1
fi
buildnumplus=$(expr $buildnum + 1)
/usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildnumplus" "$plist"

echo $(date) "- Incremented CFBundleVersion to" $buildnumplus >> hookLog.txt
查看更多
柔情千种
6楼-- · 2019-01-01 12:17

The script I'm currently using is very much based on Alix's, above. My adaptation, below, adds a check to only do the auto-increment on a release/archive build.

Without that change there will be version control conflicts as each developer will be incrementing the build number at their own rate. And the fact that the git history would be unnecessarily polluted with the build number changing all the time.

# xcode-build-bump.sh
# @desc Auto-increment Xcode target build number every time the project is archived
# @src stackoverflow.com/a/15483906
# @usage
# 1. Select: your Target in Xcode
# 2. Select: Build Phases Tab
# 3. Select: Add Build Phase -> Add Run Script
# 4. Paste code below in to new "Run Script" section
# 5. Drag the "Run Script" below "Link Binaries With Libraries"
# 6. Insure that your starting build number is set to a whole integer and not a float (e.g. 1, not 1.0)

if [ "Release" != "${CONFIGURATION}" ]
then
    exit 0
fi

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

It's also available (in a slightly easier to copy and paste format) as a GitHub gist.

查看更多
爱死公子算了
7楼-- · 2019-01-01 12:17

I feel like I've found my tribe. Tribe, I hope you're amused by VersionX.

A decade ago while working on a workspace that had over 25 Xcode projects in it I took the opportunity to automate the version and build string updates to a degree which might seem absurd, if you're maintaining only a project or two with occasional updates.

VersionX:

  • is aware of the build type (Release / Debug)
  • gathers information at build time from the repository (git support included, but can be customized for hg, svn, or whatever you use)
  • provided for easily customizable fancy marketing version strings (which had much more variation before the App Store imposed a convention) so you could automatically increments strings which included symbols for a "beta" using a git tag convention, for example.
  • includes a class populated with instance variables containing version and commit information. This is useful for populating your about panel and constructing logging strings, crash reports, or user email bug reports with pre-populated information.

It was fun to make. I learned a boatload about the Xcode build system.

Here's an example of the type of fancy Version and Build strings VersionX could automatically generate.

VersionX 1.0.1 β7 (c5959a3 “Clean”)

Marketing Version: VersionX 1.0.1 β7 The "1.0.1 is derived from the tag for the commit, while The “Beta 7” is automatically generated by the commit count, or build count (for example).

Build Version: (c5959a3 “Clean”) Displays the short commit hash, and informs you that the build directory had zero uncommitted changes.

VersionX (source at GitHub) - a baroque system for automatically incrementing version and build strings in Xcode projects.

The VersionX Documentation.

查看更多
登录 后发表回答