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:10

I would recommend the use of autorevision.

Xcode allows for a header file (which can be auto generated at build time and not in the vcs its self) to provide values that will be expanded in the info.plist at build time. You can find a walkthrough for setting this up on the autorevision website.

Autorevision has an output type geared towards these types header files to help in exactly these situations.

查看更多
千与千寻千般痛.
3楼-- · 2019-01-01 12:10

Heres an updated version. This works as of Xcode 9.3.1, iOS 11.

Click on 'Build Phases' from your application target, click the + icon to add a new run script, and in the box, paste this code.

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}"

Go into the Info.plist file and set the 'Bundle version' to 1, and the 'Bundle versions string, short' to 1, you should be set.

Build the project with Info.plist in view, and you should see the Bundle version (Build number) change.

  • Note that as of Xcode 9.3.1, you will not be able to see these changes from the general tab, but will see the changes when you archive a build, and in Info.plist
查看更多
素衣白纱
4楼-- · 2019-01-01 12:11

I don't know which way is the best, but I will post Apple's answer just in case anybody is searching for it...

According to this Apple's Q&A post:

Automating Version and Build Numbers Using agvtool

The version and build number keys respectively specify the marketing and internal versions of your application. agvtool is a command-line tool that allows you to automatically increment these numbers to the next highest number or to a specific number.

The build number identifies an unreleased or released version of your application. It is stored in your application’s Info.plist as CFBundleVersion(Bundle version).

You must complete the following steps in your Xcode project:

  1. Enable agvtool

Navigate to the Build Settings pane of your target, then update it for all your build configurations as follows:

  • Set Current Project Version to a value of your choosing.

Your Xcode project data file, project.pbxproj, includes a CURRENT_PROJECT_VERSION (Current Project Version) build setting, which specifies the current version of your project. agvtool searches project.pbxproj for CURRENT_PROJECT_VERSION. It continues running if CURRENT_PROJECT_VERSION exists and stops running, otherwise. Its value is used to update the build number.

  • Set Versioning System to Apple Generic.

By default, Xcode does not use any versioning system. Setting Versioning System to Apple Generic ensures that Xcode will include all agvtool-generated version information in your project.

Set Versioning System to Apple Generic

  1. Set up your version and build numbers

agvtool searches your application’s Info.plist for your version and build numbers. It updates them if they exist and does nothing, otherwise. Make sure that the CFBundleVersion (Bundle version) and CFBundleShortVersionString (Bundle versions string, short) keys exist in your Info.plist as seen in the image below:

Set up your version and build numbers

Quit Xcode, then navigate to the directory containing your .xcodeproj project file in the Terminal application before running any of the following commands. The .xcodeproj project file contains project.pbxproj, which is used by agvtool. (This is the part you can run in a script instead of command line.)

Updating the Version Number

To update the version number to a specific version, run

xcrun agvtool new-marketing-version <your_specific_version>

Ex: Update the version number to 2.0

xcrun agvtool new-marketing-version 2.0

Updating the Build Number

To automatically increment your build number, run

xcrun agvtool next-version -all

To set the build number of your application to a specific version, run

xcrun agvtool new-version -all <your_specific_version>

Ex: Set the build number to 2.6.9

xcrun agvtool new-version -all 2.6.9

Bonus:

To view the current version number, run

xcrun agvtool what-marketing-version

To view the current build number, run

xcrun agvtool what-version
查看更多
美炸的是我
5楼-- · 2019-01-01 12:11

You could use Apple's generic versioning. Basically all you have to do is call agvtool next-version -all from within the directory that hosts your .xcproj file. For more details check out the url above.

查看更多
萌妹纸的霸气范
6楼-- · 2019-01-01 12:14

One issue with some of these solutions is that Launch Services only recognizes four five major digits in the bundle version. I have a project with a build number that's in the thousands, so I wanted to use some of the less significant digits.

This Perl script increments all Info.plists in the project, not just the one for the current target, so the build numbers all stay in lockstep. It also uses one patch digit and two minor digits, so build 1234 is given version 1.23.4. I use it as a pre-build behavior, so it applies to all projects I build.

The script is pretty brute-force, but it does work for me.

#!/usr/bin/perl

use strict;
use warnings;
use v5.12.0;

use Dir::Iterate;

for my $plist_file(grepdir { /-Info.plist$/ } '.') {
    my $build = `/usr/libexec/PlistBuddy -c "Print CFBundleVersion" '$plist_file'`;
    chomp $build;

    next unless $build;

    # Strip dots
    $build =~ s/\.//g;
    $build =~ s/^0//g;

    # Increment
    $build++;

    # Re-insert dots
    $build =~ s/^(\d{0,4}?) (\d{0,2}?) (\d{0,1}?)$/$1.$2.$3/x;

    # Insert zeroes
    $build =~ s{(^|\.)\.}{${1}0.}g;

    system qq(/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $build" '$plist_file');
}
查看更多
还给你的自由
7楼-- · 2019-01-01 12:14

You may want to do this only when you archive (and upload to TF for example). Otherwise your version number might go up really quick..

In the scheme (Product / Edit Scheme / Archive / Pre-Actions) you can add a script that will be executed only when you archive.

Also, you might want to reset build number each time you increment the app version.

Last thing, if you use archive instead, you can safely disable:

# if [ -n "$(find "$dir" \! -path "*xcuserdata*" \! -path "*.git" -newer "$plist")" ]; then
...
# else
    # echo "Not incrementing build number as source files have not changed"
# fi

As the build number will be incremented only when you archive...

EDIT: Correct what I said, pre-actions in archive happen after build (but before archiving), so build number will be increment for next archive... But you can create a new scheme and add this action in the build (pre-actions) section of this new scheme. and use this scheme when you want to create a new build

查看更多
登录 后发表回答