Run Script While Cleaning in Xcode
A recent comment reminded me that I alluded to a way to call scripts when Cleaning a target in your Xcode project, as opposed to a normal “Run Script Build Phase”, which does not get run during a clean. So, here is how you do it. I admit, it’s a bit of a hack (in a good way, I like to think), but it gets the job done, and I couldn’t find any other way to accomplish this.
Overview
The trick is that you create a new “External Target”, and set that as a dependency of your main target. This External Target lets you run any script (if you change the “tool” from /usr/bin/make
to /bin/sh
) and, most importantly, will get run while Cleaning (with the $ACTION
environment variable set to “clean”).
Setup Instructions
- In your project, go to “Project” > “New Target…”
- Select “Mac OS X” > “Other” > “External Target” and press “Next”.
- Give it a name (in this case, I’m calling it “ExternalScript”) and press “Finish”.
- Double-click on this new target in the “Groups & Files” section to edit
its settings. The key detail here is changing the “Build Tool” to
/bin/sh
and the “Arguments” to"${PROJECT_DIR}/scripts/externalScript.sh"
(or whatever script you want to run). See my example script below. .note (Note: The quotes around the file path in the Arguments are important if there is a space in your PROJECT_DIR path.)
- Make this build phase a dependency of your main target. To do this, double-click on your main target in the “Groups & Files” section to get info on it, and go to the “General” tab. Drag your ExternalTarget into the “Direct Dependencies” list. This will make it get called every time you Build, or Clean (assuming you say “Also Clean Dependencies”) your main target.
The Script
You can have this script do anything you want, just like a “Run Script Build Phase”, but the important difference is that it gets called for Clean actions too. So, you want to make your script check for that (by checking the $ACTION environment variable). I do it like this:
externalScript.sh
#! /bin/sh
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# ACTIONS
# These are implemented as functions, and just called by the
# short MAIN section below
buildAction () {
echo "Building..."
# You might want to call your clean action first, if it makes sense.
cleanAction
# Now do build steps.
}
cleanAction () {
echo "Cleaning..."
# Do your clean steps here.
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# MAIN
echo "Running with ACTION=${ACTION}"
case $ACTION in
# NOTE: for some reason, it gets set to "" rather than "build" when
# doing a build.
"")
buildAction
;;
"clean")
cleanAction
;;
esac
exit 0
Indicating Errors
If you have your script exit with a non-zero exit value (i.e. exit
1
), Xcode will halt the build. Furthermore, if you print output that
starts with “error: ”, Xcode will show that in the Build Results window, like
so:
Notes and Limitations
- As I note in the comment in the example script, the
$ACTION
environment variable doesn’t get set to “build” when doing a build, but rather just to the empty string. - You can’t really set the order of this step within normal “Run Script Build
Phase” steps. As a dependency to your main target, it gets called first.
But, if you’re only using this so that you can call a script while cleaning,
it’s not a big deal. In that case, you might not even do anything in this
script during a build. But, you still want to check the
$ACTION
environment variable, so that you don’t run during the Build.
Alright, that’s it. Let me know if you have any questions or suggestion on how to improve this.