Ammo Navigation Weblog Company Support Store Rogue Amoeba
Rogue Amoeba
Fri, 11 Mar 2005


As I don't like to fall too far behind with the times, today I decided to start updating our code base to use the new Objective-C @try/@catch exception handling system, replacing the old NS_HANDLER macros.

To do this however, you need to enable the -fobjc-exceptions build option on each project that needs it. This wouldn't be so terrible, except we have 20 some odd different project files in our repository currently. Updating each one by hand, which I've done in the past, gets to be real painful after about the third one.

Luckily, Xcode has an amazingly complete AppleScript dictionary:

Xcode Dictionary

This seems to be a little known fact about Xcode, but it's a real life saver in situations like this. Instead of spending the day manually clicking checkboxes, I just wrote the following Batch Updater script:

Download XcodeProjectBatchUpdater.scpt

(* Xcode Project Batch Updater *** (C) Copyright 2005, Rogue Amoeba Software, LLC

    Configuration Options:
        RelaunchXcodeForEachProject - Quit and relaunch Xcode for each project (or leave it open)
*)

property RelaunchXcodeForEachProject : true

on _updateProjectDocument(aPrjDoc)
    tell application "Xcode"
        repeat with aTarget in (every target of aPrjDoc)
            --set newBuildSettings to {|GCC_ENABLE_OBJC_EXCEPTIONS|:"YES"}
            --set build settings of aTarget to ((build settings of aTarget) & newBuildSettings)
            
        end repeat
    end tell
end _updateProjectDocument

on _updateProjectAtPath(aPrjPath)
    
    set aPrjAlias to (POSIX file aPrjPath) as alias
    tell application "Xcode"
        open aPrjAlias
        --set aPrjDoc to first item of (every project document whose path starts with aPrjPath
        set aPrjDoc to current project document
        my _updateProjectDocument(aPrjDoc)
        if not RelaunchXcodeForEachProject then
            close window of aPrjDoc
        end if
    end tell
    
end _updateProjectAtPath

on _gatherUserAcceptedProjects(aSearchPath)
    set allPrjs to _gatherAllProjects(aSearchPath)
    return choose from list allPrjs with prompt "Choose the projects to update:" default items allPrjs OK button name "Update" with multiple selections allowed
    
end _gatherUserAcceptedProjects

on _gatherAllProjects(aSearchPath)
    set cmd to "find " & aSearchPath & " -type d \\( \\( -not \\( \\( -name .svn -and -prune \\) -or \\( -name build -and -prune \\) \\) \\) -and \\( -name '*.xcode' \\) \\) -print"
    
    set results to do shell script cmd
    set text item delimiters to ASCII character 13
    return text items of results
end _gatherAllProjects

on run
    
    set searchRootPath to POSIX path of (choose folder with prompt "Search for projects in:" default location (path to home folder))
    if searchRootPath ends with "/" then
        set text item delimiters to ""
        set searchRootPath to (items 1 thru ((length of searchRootPath) - 1) of searchRootPath as list) as string
        
    end if
    
    repeat with aPrjPath in _gatherUserAcceptedProjects(searchRootPath)
        if RelaunchXcodeForEachProject then
            tell application "Xcode" to quit
            delay 1
        else
            tell application "Xcode" to close every window
        end if
        
        try
            _updateProjectAtPath(aPrjPath)
        on error
            set reply to display dialog "Failed to update project: '" & aPrjPath & "'" with icon caution buttons {"Stop", "Skip"} default button 2
            
            if button returned of reply is not "Skip" then
                return
            end if
        end try
    end repeat
    
    if RelaunchXcodeForEachProject then
        tell application "Xcode" to quit
    end if
    
end run

Heres a brief walkthrough of the script, since you need to modify the script to do anything useful with it.

1. The run handler first prompts you for a folder in which to look for project files in (and below of, it's recursive).
2. _gatherAllProjects() uses do shell script and `find` to locate the project files.
3. _gatherUserAcceptedProjects() prompts for which projects to update, since odds are there are some that shouldn't be updated.
4. _updateProjectAtPath() opens the project(s) in Xcode, and finds the "project document" reference and hands it off to _updateProjectDocument()
5. _updateProjectDocument() does actual work on the project. In our example script above, it would turn the GCC_ENABLE_OBJC_EXCEPTIONS build setting on. This is probably the only handler you'll need to modify.

There is one bug workaround in the script, RelaunchXcodeForEachProject. I found that if you try and keep Xcode open the entire time, after about the 4th or 5th project, it would start throwing Internal Errors. Quitting and relaunching it for each and every project alleviates the problem (most of the time, anyhow).

As with any batch update system, I advise to take some caution with using it, as you can quickly mangle all your projects. Having a version control system or recent backups in place before hacking away, is highly recommended.

Posted by Quentin | Permalink | View/Post Comments (0)

Comments



This post is archived, and commenting has been closed.
Copyright © 2008 Rogue Amoeba Software, LLC. All rights reserved.