Running script only for an 'Archive' build in Xcode 4 - iphone

I have a script that I run using osascript from a Run Script Build Phase in Xcode 4. This script does some checks to try and catch any human-error in plists and suchlike.
However, I only really want to run this script when a developer chooses 'Archive' as opposed to running it every time they build.
Is there any way of testing for this situation? Either in Shell Script or Apple Script?

In Xcode 4+, you could run this script as a pre- or post-action script on the "Archive" action. However, pre- and post-action scripts are not considered part of the build itself; their output is not part of the build log, and a failing script will not cause the build to fail.
If you want to include this script as a first-class part of the build, such that its failure will cause the build to fail, then you'll want to do something else. Make a new "External Build System" target, and configure it to run your script. The path to your script can be a relative path, relative to the project root.
Finally, open your main project scheme, and in the "Build" tag add the new checker target, and set it to only be included in the "Archive" action. This should cause your checker script to be run as part of the build only when "Archive" is selected, and if the script returns a non-zero value the build will fail.
Here's a step-by-step visual guide to configure a bash script to run only when archiving a target:
File > New > Target > Other > External Build System
Name the product accordingly and set /bin/bash as the Build tool
Provide the path to the script under Info > Arguments of the newly created target
Product > Scheme > Edit Scheme…, and edit the scheme of the target where you want this script to run before archiving. Under Build, add the External Build System target you added in step 1. Reorder if needed. Then uncheck everything except Archive.

Add a "New Run Script Phase" to your project's "Build Phases"
Move (drag&drop) your script in the right position
Check the "Run script only when installing"
If you don't do 3. the script will run all the time, but if you check that box, it will only run when archiving.

So in latest Xcode (13.3) you can edit your schemes and copy & paste (or drag&drop) your executable script in the run script textfield (see image).
The pre-Action is executed before the Organizer is opened and the post-action is executed after the build landed in the archives list of products. In both cases the build process of archiving will run through beforehand.

A quick test on my own system shows no difference in the detailed script output between straight "Build" and "Build and Archive".
Also, you can't test for the presence (or absence) of the archive, since it only gets created at the very end of the process, when all scripts have been run.
As far as I can see, there is no current option within Xcode 3 to do this. Maybe file an enhancement request with Apple?
While you wait on Apple, the only solution I can offer is to use xcodebuild and xcrun as part of a command-line shell script, where you would know if you are archiving or not. This is not a stay-in-Xcode solution, but it does have a lot of flexibility.
Xcode "Build and Archive" from command line

Related

Xcode Copy Bundle Resources can't find files

Xcode can't find my Storyboards and my Info.plist in my Copy Bundle Resources, So my App doesn't run. I tried to add the Existing files again but they always appear red highlighted. I'm pretty sure it must be a local problem because when i clone the latest update from my repository on my other mac its runs without any problems. I already tried to re-install Xcode, delete files from Xcode/DerivedData and i also deleted the com.apple.Xcode.plist.
Anyone any ideas?
Try to reset your Simulator and then clean your App Build Folder
My experience is that the proposed solution works, but cleaning and re-compiling the entire app whenever a resource has changed is very tedious, especially for larger projects.
Therefore I came up with this solution that forces fresh resources in the app on a per-directory basis, without having to clean or recompile:
Add a 'Run Script Build Phase' (Editor > Add Build Phase > Add Run Script Build Phase)
Copy/paste the following script into the build phase (don't forget to set the actual paths on line 1):
dirsToCopy=("path1/directory1","path2/directory2")
for INPATTERN in "${dirsToCopy[#]}"; do
INDIR=$PROJECT_DIR/$INPATTERN/*
OUTDIR=$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/$INPATTERN
cp -R $INDIR $OUTDIR
done
For those not used to working with shell scripts:
paths on line 1 of the script are relative to the project directory (where the .xcodeproj file resides)
you can add more (or less) paths to the array dirsToCopy if you want
you can change the pattern of files to copy where variable INDIR is defined
you can change how the copying is done (currently, recursive due to -R) by changing the flags to cp in the last line of script within the for block.

PyDev Unit Test Set Run Directory

I have a PyDev unit test module that lives at the path:
$(PYDEV_PROJECT_ROOT)/tests/my_unit_test.py
I am attempting to use Eclipse PyDev's unit testing facilities. My unit test must read a configuration file like so:
(foo,bar,baz) = myModule.readOptimizationConfig("tests/optimization_config_file.cfg")
However, this will not work because PyDev goes into the 'tests' directory before running, and so specifying 'tests/' in the path given to readOptimizationConfig makes it attempt to load
$(PYDEV_PROJECT_ROOT)/tests/tests/optimization_config_file.cfg
However, I also need to run these tests using nosetests from the command lin.
This is because, in order to run ALL the tests for my project, rather than the option for running them in a particular file that is provided by default, the easiest solution was to just use the 'nosetests' command, rather than messing with Eclipse launch configurations. However, nosetests needs to be be run from the $(PYDEV_PROJECT_ROOT) root directory, so it needs the 'tests/' specified in the path.
Is there a way to force eclipse to run the unit tests from the project root directory, so that the paths that I pass to readOptimizationConfig will work for both methods?
It is possible to do this in PyDev, but you have to do it per every launch configuration so it's a bit boring.
Anyway, you first try to run as your script containing the unit-test (as you normally would - e.g. the dropdown menu next to the green "Run" arrow button then Run as... - Python unit-test). This launch will fail because of your missing cfg file. Now go to Run configurations (Run dropdown - Run configurations), open the Arguments tab and in the bottom under Working directory enter the path you want (or browse for it using the Workspace... button). For example if you want to run from project root and your project is called awesome-project, you would write:
${workspace_loc:awesome-project}
Now you should have a valid launch configuration that you can use from both the Run and Debug menus. I sometimes rename these configurations to something noticeable right away e.g. "awesome-project TEST".

How to get the app path name from an Xcode project on the command line?

I'm writing a build script to compile and package my app, and I'd like a nice way to get the full path name of the .app created. I can't find any command line tools other than xcodebuild, which doesn't appear to have much in the way of inspecting an Xcode project. My full compile command is
xcodebuild -sdk iphoneos2.2.1
so it'll build with the default configuration, and I don't want to hard-code the .app filename in (although it'll be something like build/<config>iphoneos/<name>.app). Currently, I'm parsing the output from the xcodebuild command and grabbing the line
CodeSign (.*)
which works correctly, but seems like an awfully roundabout way of doing it. Is there another command line tool to do this, or at least an easier way than my solution?
Tough to say since the build location can be project/target specific or a global preference that's never explicitly set anywhere in the project file.
Never tried it, but maybe add a Run Script build phase to your project that simply prints the environment to stdout and parse that?

Run script during Clean / Clean All in Xcode

I have a fairly complex (iPhone SDK) Xcode project, with many targets -- 4 static libs, unit tests, multiple sample apps, a BuildAll that runs a shell script, and a Package that runs another shell script. The "BuildAll" target creates a directory in the project with some subdirectories with contents ready for distribution.
When I click "Clean All," though, Xcode doesn't know to clean my Distribution directory. I'd like it to. I can't seem to find a way to do this -- does anybody know how?
It feels like Clean and Clean All should really just be targets in Xcode, and I should be able to add a "Run Script" phase. Not so, to my knowledge.
BTW, the "BuildAll" target does handle cleaning the Distribution directory, so this is not the end of the world to me. It's just irksome that "Clean All" doesn't actually clean all in my particular case.
You can try to Add > New Target… > Shell Script Target by control-clicking on a Targets node in the Groups & Files.
Then, after double-clicking on a Run Script node you setup any cleanup-scenario using $ACTION variable that can have values clean, install, etc.
And finally, newly created Shell Script Target should be added to the main target as dependency.
You can add an "External Target" that does the cleaning in an external script, and add that target as a dependency of one of your static library targets. Create the target, and drag it under one of your static library targets. Check for the action using the $ACTION environment variable in the script.

Copy file to the App Resources directory if debug configuration is selected

I need to copy a few files into the App's Resources directory during debug builds. I am thinking about using build rules but don't know how to determine if the build is a debug build. I do have a compiler option of "DEBUG" set.
You can use a Run Script build phase to do the copying. All build settings applied when building the target are available via environment variables in your script.
You can determine what configuration is being built via the CONFIGURATION environment variable; you can look at other environment variables like BUILT_PRODUCTS_DIR to determine where to put your resource. If you specify your Run Script build phase's output correctly, it will only be run when the output needs to be brought up to date, not every time you build.
More information on Run Script build phases is available here: Xcode Build System Guide: Build Phases: Run Script Build Phase
The same kind of thing can be done with script build rules, which is useful if you have multiple resources you want to apply this to: You can create a script build rule that matches your extension (e.g. *.myresource) and use the build settings and input files that are passed to your script via environment variables to do the actual copying. If you specify your build rule's output correctly, it will only be run when its input is newer than its output, not every time you build.
More information on script build rules is available here: Xcode Build System Guide: Build Phases: Build Rules