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?
FINAL EDIT: I now do this kind of stuff using a python script which I have just made public on github. It's not well documented but shouldn't be difficult to work out. As a bonus this repo also contains a useful script to automatically bundle 3rd party library into an app bundle.
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!
We simply set the version number for the built product to the number of Git commits. This won't mess with your source control, since the script only mutates the built product.
Add this "Run Script" build phase to the end of your build phases:
if [ "${CONFIGURATION}" = "Release" ]; then
buildNumber=$(git rev-list --count head)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
fi
Set your Info.plist version in your project to whatever you want, it will never get used when building a release build. I set mine to AUTOMATED or DEVELOPMENT so it's clear when I'm running a development build.
That's it! The built app will have a constantly increasing build number. (As long as you always do your builds off the same branch.)
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
Other notes:
If you have app extensions in your project, simply set the same build script on those targets too. This will keep all the version numbers automated and in sync. The App Store requires extension versions match your main app.
I have used this glist. It works as expected.
https://gist.github.com/sekati/3172554
(all credit goes to the original author)
Scripts that I modified over time.
xcode-versionString-generator.sh,
xcode-build-number-generator.sh
As these gist are helping the dev community, I made GitHub project out of it. So let's develop it well.
Here is the GitHub project:
https://github.com/alokc83/Xcode-build-and-version-generator
I have updated the code for both script little bit of enhancement.
instead of using below grab the latest from GitHub
For Version :
# xcode-version-bump.sh
# #desc Auto-increment the version number (only) when a project is archived for export.
# #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. Check the checkbox "Run script only when installing"
# 6. Drag the "Run Script" below "Link Binaries With Libraries"
# 7. Insure your starting version number is in SemVer format (e.g. 1.0.0)
# This splits a two-decimal version string, such as "0.45.123", allowing us to increment the third position.
VERSIONNUM=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${PROJECT_DIR}/${INFOPLIST_FILE}")
NEWSUBVERSION=`echo $VERSIONNUM | awk -F "." '{print $3}'`
NEWSUBVERSION=$(($NEWSUBVERSION + 1))
NEWVERSIONSTRING=`echo $VERSIONNUM | awk -F "." '{print $1 "." $2 ".'$NEWSUBVERSION'" }'`
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $NEWVERSIONSTRING" "${PROJECT_DIR}/${INFOPLIST_FILE}"
For build:
# xcode-build-bump.sh
# #desc Auto-increment the build number every time the project is run.
# #usage
# 1. Select: your Target in Xcode
# 2. Select: Build Phases Tab
# 3. Select: Add Build Phase -> Add Run Script
# 4. Paste code below into new "Run Script" section
# 5. Drag the "Run Script" below "Link Binaries With Libraries"
# 6. Ensure that your starting build number is set to a whole integer and not a float (e.g. 1, not 1.0)
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}"
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.
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
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:
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 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:
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
FWIW - this is what I'm currently using to increase the build number only for release builds (which includes archiving). Works fine under Xcode 5.1.
Just copy/paste the snippet into a Run script build phase directly in Xcode:
buildnum=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$PRODUCT_SETTINGS_PATH")
if [ "$CONFIGURATION" = "Release" ]; then
buildnum=$((buildnum + 1))
echo "Build number updated to $buildnum"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildnum" "$PRODUCT_SETTINGS_PATH"
fi;
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
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.
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.
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');
}
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.
Building on Wil Gieseler's solution, I had just one change I wanted to make. His solution puts the count of git commits into the build number. Useful, but still kind of a pain to find the actual commit that created that build. I didn't care too much whether the build number was monotonically increasing, and so I dropped that requirement so that I could more easily access the commit that generated a given binary.
To that end, I modified his first script to the following:
# Set the build number to the decimal conversion of the short version of the current git SHA
# Get the short version of the current git SHA in hexadecimal
SHA=$(git rev-parse --short #)
# Uppercase any alphabetic chars in SHA (bc doesn't like lowercase hex numbers)
UPPERCASE_SHA=$(tr '[:lower:]' '[:upper:]' <<< "$SHA")
# Use bc to convert the uppercase SHA from hex to decimal
BUILD_NUM=$(bc <<< "ibase=16;obase=A;$UPPERCASE_SHA")
# Set our build number to that
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUM" "${PROJECT_DIR}/${INFOPLIST_FILE}"
# To convert a build number back to a usable git SHA, run the following, substituting the build number for <build number>
# bc <<< "ibase=10;obase=16;<build number>"
This converts the short version of the current git SHA into decimal. Hexadecimal characters don't play nicely with Apple's build number requirements, which is why I had to do this. To convert it back, you'd simply run something like this:
SHA=$(bc <<< "ibase=10;obase=16;<build number>")
in bash, where <build number> is the build number you got from a binary. Then, just run git checkout $SHA, and there you go.
Because this is an adaptation of Wil Gieseler's solution, as mentioned above, you'll also need the following post-build script:
# Set the build number to "DEVELOPMENT"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion DEVELOPMENT" "${PROJECT_DIR}/${INFOPLIST_FILE}"
which keeps your git history clean.
I tried the modified procedure and it did not work, because:-
Xcode 4.2.1 changes the xcuserdata subdirectory in .xcodeproj
git notes the previous change in Project-Info.plist
The following modification causes these to be ignored and only flags genuine changes:-
if [ -n "$(find $dir \! -path "*xcuserdata*" \! -path "*.git" -newer $plist)" ]; then
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
I use the last SVN revision for the build number. If you change the Info.plist in the build directory, you won't affect the source Info.plist:
# use the last SVN revision as the build number:
Info_plist="$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Info.plist"
defaults write "${Info_plist}" CFBundleVersion `svn stat -u | awk '/'"Status against revision:"'/ {print $4}'`
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
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.
You might want to check out a new tool I've been developing called Xcodebump. It can handle updating both CFBundleShortVersionString and CFBundleVersion. As a final step it will also checkin to git and tag the commit to match up with those CFBundle values.
The Xcodebump project is located here:
https://github.com/markeissler/Xcodebump
I update build number by following method.
$INFO_FILE is the path of the plist file.
And $build_number is a new build number for this building.
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $build_number" "${INFO_FILE}"
Generally, my $build_number is composed by major and minor parts.
The minor is come from project information. So I describe how to generate the major part.
## Composed by `major` and `minor`.
## `minor` is parsed from project information. It's another story.
## Examples: `21.1`, or `21.1.3`
build_number="${major_number}.${minor_number}"
I have 2 strategies to decide the $build_number.
First Strategy
This strategy uses the git tag count to decide the major of build number. If there are 53 tags of the project, it will return 53 by following shell script.
Generally, it's increasing. And it will force the developer to put a git tag before publishing.
major_number=$(git tag -l | wc -l | grep -oE "\d+")
Second Strategy
Let Jenkins CI system decide the major part. It has an environment variable BUILD_NUMBER. It is increasing automatically when building on the CI system. This information is useful to trace the project history on the CI system.
major_number=${BUILD_NUMBER}
Here's my solution.
If you're like me: terminal friendly, like ruby, like semantic versioning, try this.
Make a file named Rakefile which contains this:
require "xcodeproj"
require "versionomy"
XCODEPROJECT = "MyProject.xcodeproj"
INFOPLISTFILE = "MyProject/MyProject-Info.plist"
$UPDATES = [:major,:minor,:tiny]
$UPDATES.each { |part|
desc "increment #{part} part of version"
task "increment:#{part}" do |task|
version=`/usr/libexec/Plistbuddy -c "Print CFBundleVersion" #{INFOPLISTFILE}`.chomp
version=Versionomy.parse(version)
version=version.bump(part)
# I use the same string for CFBundleVersion and CFBundleShortVersionString for now
`/usr/libexec/PlistBuddy -c "Set :CFBundleVersion #{version}" #{INFOPLISTFILE}`
`/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString #{version}" #{INFOPLISTFILE}`
print "version upgraded to #{version}\n"
end
}
Prepare: gem install xcodeproj versionomy
Run: rake increment:major or rake increment:minor or rake increment:tiny whenever you want.
I find it most convinient to use Automating Version and Build Numbers Using agvtool.
Try this:
Configure it as described in Apple's documentation linked above.
Add script as Pre-action to Project -> Edit Scheme... -> Archive (or other if you prefer)
Set: Provide build settings from <your_app_target>
The script (first line is optional):
exec > ${PROJECT_DIR}/prebuild.log 2>&1
cd ${PROJECT_DIR}
xcrun agvtool next-version -all
cd -
Let's do this in Apple's own way.
It will increase the build number after each successful build
I will guide you through 5 images, just go through it.
Select 'Edit Scheme...' from the dropdown, when you select your Project name located in right side to the Stop_build_button.
Check First Step
From leftSide menu expand the 'Build' option and select 'Post-actions'
Check Second Step
Here you can add your desired Codes(Scripts) you want to execute after successful build of your program. It is the place where we have to add a little amount of code to make our automation work perfectly. >> 1. select the 'add (+)' button from leftSide corner to add new script file >> 2. Now from the drop down select 'New Run Script Action'
Check Third Step
It has 3 fields >> 1. shell is already assigned for you >> 2. now for 'Provide you build settings from' Select your Project Name. >> 3. Theres a big field to add your Script, just copy and past this code there : Check Fourth Step
PLIST="${PROJECT_DIR}/${INFOPLIST_FILE}"
PLB=/usr/libexec/PlistBuddy
LAST_NUMBER=$($PLB -c "Print CFBundleVersion" "$PLIST")
NEW_VERSION=$(($LAST_NUMBER + 1))
$PLB -c "Set :CFBundleVersion $NEW_VERSION" "$PLIST"
After completing the 4th step just select 'Close' to close the window and we have to do the last step, Goto your 'plist.info' file in Project file menu and make sure 'Bundle Version' key under 'Key' Section most contain a Numeric Value Check Fifth Step
Related
I have an iOS for which I want to run a build phase that reads a value from a JSON file, export that as an environment variable and then read that in my Info.plist.
I currently have:
# Build Scripts/SetAPIVersion
set -e
if ! which jq > /dev/null; then
echo "error: jq is missing. Be sure to git pull `dev-box` and run apply.sh"
exit 1
fi
export API_VERSION =$(cat ../src/version.json | jq .api)
echo "Set API Version to $(API_VERSION)!"
My application will build however the value does not appear to be set. What am I doing wrong here?
You can use this:
plutil -replace APIVersion -string <VERSION> <PATH TO INFO>.plist
Also you can use PlistBuddy:
/usr/libexec/PlistBuddy -c "Set :APIVersion string <VERSION>" <PATH TO INFO>.plist
If versions are static numbers depending on environment, you can use project build settings user defined variable to:
The shell interpreter is run as a subprocess. When it exports an environment variable, that only affects that shell interpreter process and its subprocesses, but doesn't affect the parent process (i.e. Xcode) nor its sibling processes (other build phases).
You can make the shell script build phase take an input file, say Info.plist.in, and produce Info.plist from that. It would transform the input to the output however you like. For example, it could use sed to replace a special string with the value it should have. Be sure to configure the inputs and outputs of the run-script build phase as appropriate.
Alternatively, you could have the run-script build phase produce a header file that defines a macro, say api_version.h which #defines API_VERSION, #include that header file in your Info.plist, and enable preprocessing of Info.plist in the build settings. Again, make sure the inputs and outputs of the run-script phase are correct.
Is there a way of grabbing the current scheme from a run script phase?
I've tried $(SCHEME_NAME) but it doesn't exist.
I couldn't find an environment variable to use, so I had to develop a work around: write a scheme name to disk in the Build Pre-action and then read it back out in the Run Script phase.
For each scheme you're interested in go to Edit Scheme and add a script with the following code:
rm -rf ${INTERMEDIATES_OUTPUT_DIR}
mkdir -p ${INTERMEDIATES_OUTPUT_DIR}
echo MY_SCHEME_NAME > ${SCHEME_FILE}
Next, go to your build target's "Build Settings" and add two "User-Defined Settings":
INTERMEDIATES_OUTPUT_DIR=${PROJECT_DIR}/build/intermediates/${CONFIGURATION}/
SCHEME_FILE=${INTERMEDIATES_OUTPUT_DIR}current_scheme.txt
Open up your "Run script" and add this:
SCHEME_NAME=`cat ${SCHEME_FILE}`
Be sure to add the intermediates build directory to your .gitignore file.
Obviously you could simplify this a bit by hardcoding a file name but this is a bit more robust (and we have other stuff that ends up in the intermediates directory, too).
Simplest one is set 'User Defined' variable as per scheme and use it.
You can write the scheme name to the info.plist file and read it back in Run Script Phase, in Edit Scheme menu, choose Build -> Pre-actions and add the following script:
/usr/libexec/PlistBuddy -c "Set :SchemeName \"$SCHEME_NAME\"" "$INFOPLIST_FILE"
and then add the key SchemeName of type string in the info.plist, and its initial value is empty.
Finally, in the run script phase add the following:
SCHEME_NAME=$(/usr/libexec/PlistBuddy -c "Print SchemeName" "$INFOPLIST_FILE")
INTERMEDIATES_OUTPUT_DIR doesn't seem to work for building swift previews which I wanted customization on as well. I ended up doing something similar using defaults write which I found works in all cases and doesn't involve the creation of a file.
Prebuild:
defaults write com.apple.dt.Xcode LastBuildScheme "MySchemeName"`
Build Script:
[[ $(defaults read com.apple.dt.Xcode LastBuildScheme) = "MySchemeName" ]] && echo "Scheme" || echo "Not Scheme"
Finally... after a couple years of watching and a month of participating, I have a chance to ask you guys a question of my own.
My boss doesn't trust me (or any process) to increment a build number, he also wants to have a build date & time baked into the app. I'd like to put this into the usual Info.plist file.
I found this related question:
Build information in iOS Application (date/time app was built)
and based on the answers there, I went into the Scheme Editor and added the script below to the "Post-Action" section of the Build phase:
infoplist="$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH"
builddate=`date`
if [[ -n "$builddate" ]]; then
defaults write "${infoplist%.plist}" BuildDate "${builddate}"
fi
In XCode, my Scheme Editor window looks like this:
(source: myke at maniac.deathstar.org)
Unfortunately, BuildDate never gets written into Info.plist.
Changing "${builddate}" to "$builddate" doesn't work either. I added this line to the script:
echo "build date is $builddate" > /tmp/result.txt
and the date appeared perfectly fine in the written out file. Writing strings into the Info.plist file from the above script works perfectly fine, annoyingly enough.
So, summed up, how to get the date to be added to the Info.plist file?
Ahhhh, I should have spent another 30 minutes (on top of the 2 hours I had already wasted) and looked at the answers for this question before posting my own:
Insert Subversion revision number in Xcode
This post-action script does the trick and works for me:
infoplist="$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH"
builddate=`date`
if [[ -n "$builddate" ]]; then
# if BuildDateString doesn't exist, add it
/usr/libexec/PlistBuddy -c "Add :BuildDateString string $builddate" "${infoplist}"
# and if BuildDateString already existed, update it
/usr/libexec/PlistBuddy -c "Set :BuildDateString $builddate" "${infoplist}"
fi
As you can see, it's doing a bit of a hack there (adding it if it doesn't exist; setting it right afterwards).
If anyone can suggest a solution using the "defaults write" method above (which I think might be better supported than "PlistBuddy"), I'd be thrilled to find out (and of course I'll accept and use that superior answer, too).
The code in Michael's answer is incorrect or no longer up to date. The version below fixes an error in the set syntax and also supports build paths with spaces in them.
infoplist="$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH"
builddate=`date`
if [[ -n "$builddate" ]]; then
# if BuildDateString doesn't exist, add it
/usr/libexec/PlistBuddy -c "Add :BuildDateString string $builddate" "${infoplist}"
# and if BuildDateString already existed, update it
/usr/libexec/PlistBuddy -c "Set :BuildDateString $builddate" "${infoplist}"
fi
Note: This change was submitted as an edit but got rejected and I don't yet have enough reputation to post a comment on his answer...
I am using your exact code, but within the pre-action instead of the post-action, and the info.plist within the built product correctly reports the build date. In other words, you have to customize your info.plist before copying it into the built product, which sounds reasonable to me.
By the way, thanks for the suggestion, it's pretty clever and useful.
I would like to remove the digital signature from a Mac app that has been signed with codesign. There is an undocumented option to codesign, --remove-signature, which by it's name seems to be what I need. However, I can't get it to work. I realize it is undocumented, but I could really use the functionality. Maybe I'm doing something wrong?
codesign -s MyIdentity foo.app
works normally, signing the app
codesign --remove-signature foo.app
does disk activity for several seconds, then says
foo.app: invalid format for signature
and foo.app has grown to 1.9 GB!!! (Specifically, it is the executable in foo.app/Contents/Resources/MacOS that grows, from 1.1 MB to 1.9 GB.)
The same thing happens when I try to sign/unsign a binary support tool instead of a .app.
Any ideas?
Background: This is my own app; I'm not trying to defeat copy protection or anything like that.
I would like to distribute a signed app so that each update to the app won't need user approval to read/write the app's entries in the Keychain. However, some people need to modify the app by adding their own folder to /Resources. If they do that, the signature becomes invalid, and the app can't use it's own Keychain entries.
The app can easily detect if this situation has happened. If the app could then remove it's signature, everything would be fine. Those people who make this modification would need to give the modified, now-unsigned app permission to use the Keychain, but that's fine with me.
A bit late, but I've updated a public-domain tool called unsign which modifies executables to clear out signatures.
https://github.com/steakknife/unsign
I ran into this issue today. I can confirm that the --remove-signature option to Apple's codesign is (and remains, six years after the OP asked this question) seriously buggy.
For a little background, Xcode (and Apple's command line developer tools) include the codesign utility, but there is not included a tool for removing signatures. However, as this is something that needs to be done in certain situations pretty frequently, there is included a completely undocumented option:
codesign --remove-signature which (one assumes, given lack of documentation) should, well, be fairly self-explanatory but unfortunately, it rarely works as intended without some effort. So I ended up writing a script that should take care of the OP's problem, mine, and similar. If enough people find it here and find it useful, let me know and I'll put it on GitHub or something.
#!/bin/sh # codesign_remove_for_real -- working `codesign --remove-signature`
# (c) 2018 G. Nixon. BSD 2-clause minus retain/reproduce license requirements.
total_size(){
# Why its so damn hard to get decent recursive filesize total in the shell?
# - Darwin `du` doesn't do *bytes* (or anything less than 512B blocks)
# - `find` size options are completely non-standardized and doesn't recurse
# - `stat` is not in POSIX at all, and its options are all over the map...
# - ... etc.
# So: here we just use `find` for a recursive list of *files*, then wc -c
# and total it all up. Which sucks, because we have to read in every bit
# of every file. But its the only truly portable solution I think.
find "$#" -type f -print0 | xargs -0n1 cat | wc -c | tr -d '[:space:]'
}
# Get an accurate byte count before we touch anything. Zero would be bad.
size_total=$(total_size "$#") && [ $size_total -gt 0 ] || exit 1
recursively_repeat_remove_signature(){
# `codesign --remove-signature` randomly fails in a few ways.
# If you're lucky, you'll get an error like:
# [...]/codesign_allocate: can't write output file: [...] (Invalid argument)
# [...] the codesign_allocate helper tool cannot be found or used
# or something to that effect, in which case it will return non-zero.
# So we'll try it (suppressing stderr), and if it fails we'll just try again.
codesign --remove-signature --deep "$#" 2>/dev/null ||
recursively_repeat_remove_signature "$#"
# Unfortunately, the other very common way it fails is to do something? that
# hugely increases the binary size(s) by a seemingly arbitrary amount and
# then exits 0. `codesign -v` will tell you that there's no signature, but
# there are other telltale signs its not completely removed. For example,
# if you try stripping an executable after this, you'll get something like
# strip: changes being made to the file will invalidate the code signature
# So, the solution (well, my solution) is to do a file size check; once
# we're finally getting the same result, we've probably been sucessful.
# We could of course also use checksums, but its much faster this way.
[ $size_total == $(total_size "$#") ] ||
recursively_repeat_remove_signature "$#"
# Finally, remove any leftover _CodeSignature directories.
find "$#" -type d -name _CodeSignature -print0 | xargs -0n1 rm -rf
}
signature_info(){
# Get some info on code signatures. Not really required for anything here.
for info in "-dr-" "-vv"; do codesign $info "$#"; done # "-dvvvv"
}
# If we want to be be "verbose", check signature before. Un/comment out:
# echo >&2; echo "Current Signature State:" >&2; echo >&2; signature_info "$#"
# So we first remove any extended attributes and/or ACLs (which are common,
# and tend to interfere with the process here) then run our repeat scheme.
xattr -rc "$#" && chmod -RN "$#" && recursively_repeat_remove_signature "$#"
# Done!
# That's it; at this point, the executable or bundle(s) should sucessfully
# have truly become stripped of any code-signing. To test, one could
# try re-signing it again with an ad-hoc signature, then removing it again:
# (un/comment out below, as you see fit)
# echo >&2 && echo "Testing..." >&2; codesign -vvvvs - "$#" &&
# signature_info "$#" && recursively_repeat_remove_signature "$#"
# And of course, while it sometimes returns false positives, lets at least:
codesign -dvvvv "$#" || echo "Signature successfully removed!" >&2 && exit 0
Here's the source for codesign which lists all options, including those not covered by the command-line -h and man page.
Also, here is Apple's tech note on recent changes in how code-signing works
I agree that there's something strange going on when you did --remove-signature.
However, instead of trying to un-code-sign, you should change the way your user put extra files in the Resources. Instead, designate a certain path, usually
~/Library/Application Support/Name_Of_Your_App/
or maybe
~/Library/Application Support/Name_Of_Your_App/Resources/
and ask the user to put extra files there. Then, in your code, always check for the directory in addition to the files in the Resources when you need to read a file.
On a second reading of this question, another thought: perhaps a better approach to accomplish what the ultimate goal of the question is would be not to remove the signatures, but to have users (via a script/transparently) re-sign the app after modification, using an ad-hoc signature. That is, codesign -fs - [app], I believe. See https://apple.stackexchange.com/questions/105588/anyone-with-experience-in-hacking-the-codesigning-on-os-x
I am looking for a tool that will help me to compile a history of certain code metrics for a given project.
The project is stored inside a mercurial repository and has about a hundred revisions. I am looking for something that:
checks out each revision
computes the metrics and stores them somewhere with an identifier of the revision
does the same with the next revisions
For a start, counting SLOCs would be sufficient, but it would also be nice to analyze # of Tests,TestCoverage etc.
I know such things are usually handled by a CI Server, however I am solo on this project and thus haven't bothered to set up a CI Server (I'd like to use TeamCity but I really didn't see the benefit of doing so in the beginnig). If I'd set up my CI Server now, could it handle that?
According to jitter's suggestion I have written a small bash script running inside cygwin using sloccount for counting the source lines. The output was simply dumped to a textfile:
#!/bin/bash
COUNT=0 #startrev
STOPATREV = 98
until [ $COUNT -gt $STOPATREV ]; do
hg update -C -r $COUNT >> sloc.log # update and log
echo "" >> sloc.log # echo a newline
rm -r lib # dont count lib folder
sloccount /thisIsTheSourcePath | print_sum
let COUNT=COUNT+1
done
You could write a e.g. shell script which
checks out first version
run sloccount on it (save output)
check out next version
repeat steps 2-4
Or look into ohloh which seems to have mercurial support by now.
Otherwise I don't know of any SCM statistics tool which supports mercurial. As mercurial is relatively young (since 2005) it might take some time until such "secondary use cases" are supported. (HINT: maybe provide a hgstat library yourself as there are for svn and csv)
If it were me writing software to do that kind of thing, I think I'd dump metrics results for the project into a single file, and revision control that. Then the "historical analysis" tool would have to pull out old versions of just that one file, rather than having to pull out every old copy of the entire repository and rerun all the tests every time.