Tuesday, July 26, 2011

Working for MartianCraft

In case you missed my tweets earlier this week, MartianCraft is looking to add a few developers. We're looking for a couple of experienced developers, and are also thinking about bringing in one or two entry-level devs without significant experience to be trained up.

Initially, the work would be project-based contracting and would start in late August. Conversion to full-time employment is a possibility, but not right away.

If you're interested in being considered, send an e-mail with relevant work experience and/or résumé/CV to work@martiancraft.com.



Thursday, July 14, 2011

Auto-Incrementing Build Numbers for Release Builds in Xcode

I use the wonderful Test Flight to distribute builds. One thing that Test Flight is a little picky about is build numbers. When you upload a build, it uses the build number to see if you're uploading a replacement or a new build. It will let you create a new build even if you don't remember to increment the build number, but it's an extra manual step, and then you end up with two builds with the same build number.

Because I'm forgetful, I wanted to automated this process. Basically, I wanted to increment the version short string any time we do an Archive and increment the bundle build ID any time we do a Release configuration build, but leave the version numbers alone on Debug builds.

Unfortunately, several of our projects are ones that we inherited or took over, so not every project uses the same version numbering scheme. How we increment 1.0b5 is different from how we increment 1.0.12, or a simple build number like 1058.

The way I deal with this is a Run Script Build Phase in my application's executable target that runs the following Ruby script (make sure you set the "shell" field to /usr/bin/ruby, and make sure the script is the last build phase in the application). Feel free to use this script if you wish and modify it to meet your needs. If you improve it, I'd be glad to incorporate improvements back into it. One item of note: the way that I differentiate between Archive builds and other Release configuration builds might be a bit fragile since I'm relying on an undocumented naming pattern in an environment variable.

Note: I'm aware of agvtool. I avoided it for two reasons. First, I wanted more control over the numbering schemes, and second, I tried using agvtool in a build script a few years back, but at that time, there were issues when you bumped the version numbers of a project that was currently open. Those issues may have been resolved, but I didn't want to fight that battle again.

def get_file_as_string(filename)
    data = ''
    f = File.open(filename, "r") 
    f.each_line do |line|
        data += line
    end
    return data
end


def handle_alpha_beta(old_value, letter, infoplist, start_of_value, end_of_value)
    parts = old_value.split(letter)
    version_num = parts[0]
    alpha_num = parts[1].to_i

    alpha_num = alpha_num + 1
    new_version = version_num.to_s + letter + alpha_num.to_s
    print "Assigning new version: " + new_version + "\n"
    new_key = "<string>#{new_version}</string>"

    part_1 = infoplist[0, start_of_value - '<string>'.length];
    part_2 = new_key 
    part_3 = infoplist[end_of_value + "</string>".length, infoplist.length - (end_of_value - start_of_value + (new_key.length - 1))]    

    new_info_plist = part_1 + part_2 + part_3
    new_info_plist
end

def find_and_increment_version_number_with_key(key, infoplist)

    start_of_key = infoplist.index(key)
    start_of_value = infoplist.index("<string>", start_of_key) + "<string>".length
    end_of_value = infoplist.index("</string>", start_of_value)
    old_value = infoplist[start_of_value, end_of_value - start_of_value]

    print "Old version for " + key + ": " + old_value + "\n"
    print old_value.class.to_s + "\n"
    old_value_int = old_value.to_i
    print old_value_int.class.to_s + "\n"
    if (old_value.index("a") != nil) # alpha
        infoplist = handle_alpha_beta(old_value, "a", infoplist, start_of_value, end_of_value)
    elsif (old_value.index("b") != nil) # beta
        infoplist = handle_alpha_beta(old_value, "b", infoplist, start_of_value, end_of_value)
    elsif (old_value.index(".") != nil) # release dot version
        parts = old_value.split(".")
        last_part = parts.last.to_i
        last_part = last_part + 1
        parts.delete(parts.last)

        new_version = ""
        first = true
        parts.each do |one_part|
            if (first)
                first = false
            else
                new_version = new_version + "."
            end
            new_version = new_version + one_part
        end
        new_version = new_version.to_s + "." + last_part.to_s
        print "New version: " + new_version.to_s + "\n"
        new_key = "<string>#{new_version}</string>"
        infoplist = "#{infoplist[0, start_of_value - '<string>'.length]}#{new_key}#{infoplist[end_of_value + '</string>'.length, infoplist.length - (end_of_value+1)]}"
    elsif (old_value.to_i != nil) # straight integer build number
        new_version = old_value.to_i + 1
        print "New version: " + new_version.to_s + "\n"
        new_key = "<string>#{new_version}</string>"

        part_1 = infoplist[0, start_of_value - '<string>'.length]
        part_2 = new_key
        part_3 = infoplist[end_of_value + "</string>".length, infoplist.length - (end_of_value+1)]
        infoplist = part_1 + part_2 + part_3
    end 
    infoplist
end



config = ENV['CONFIGURATION'].upcase
config_build_dir = ENV['CONFIGURATION_BUILD_DIR']

archive_action = false
if (config_build_dir.include?("ArchiveIntermediates"))
    archive_action = true
end

print "Archive: " + archive_action.to_s + "\n"


print config

if (config == "RELEASE")
    print " incrementing build numbers\n"
    project_dir = ENV['PROJECT_DIR']
    infoplist_file = ENV['INFOPLIST_FILE']
    plist_filename = "#{project_dir}/#{infoplist_file}"

    infoplist = get_file_as_string(plist_filename)
    infoplist = find_and_increment_version_number_with_key("CFBundleVersion", infoplist)
    if (archive_action)
        infoplist = find_and_increment_version_number_with_key("CFBundleShortVersionString", infoplist)
    end
    File.open(plist_filename, 'w') {|f| f.write(infoplist) }
else
    print " not incrementing build numbers"
end




Thursday, July 7, 2011

Happenings and Prospects

I apologize for the relative dearth of posts here since WWDC. That week in San Francisco always tends to backlog me pretty badly (I returned from WWDC at inbox 1138 - and that's after spending the return flight answering e-mails), so I've been pretty much heads down on work-related stuff ever since. I've also, at the same time, been consciously trying to back away from the 12-15 hour days, 7 days a week schedule that I'd fallen into trying to help get MartianCraft off the ground. Those two have conspired to give me very little time for writing lately, but I think things are under control now.

I've got my semi-mythical OpenGL ES from the Ground Up 10th installment on skeletal animation nearly finished and hope to have it posted within the next couple of weeks, and it's bit of a doozy. My goal with this post is to make one of the more intimidating topics in graphics programming approachable. Fingers crossed, it's been a challenge making this topic approachable, but I think I'm getting there.

A few other bits of news.

First, work has officially started on Beginning iPhone 5 Development. Yes, I know it probably should be called Beginning iOS 5 Development, but as of right now, we're sticking with the naming sequence Apress established with the first book. Dave, Jack, and I have already started updating the book for Xcode 4, ARC, Storyboards, and all the other cool new stuff and are hoping to have the book ready to go to press when the GM version of iOS 5 ships this fall.

Second, I purchased a new domain today. There's nothing there yet, but OpenGLESBook.com is now mine, and I have big plans for it. Once Beginning iPhone 5 Development is in the can, I'm going to revisit my partially written OpenGL ES 2.0 book. I plan to revise it to use GLKit and to add material about the very cool new OpenGL ES tools we're getting with iOS 5.

As of right now, my plan is to self-publish. I'm still researching the exact process, tools, and services that I'll use, but my plan is to sell the book without DRM and at a reasonable price. I'd like to have an early access program, however since a lot of the material I'll be covering will be under NDA until iOS 5 goes GM, I can't promise that at this time.



Update on UpdateConf

Just wanted to let you know that I'm speaking at another conference. I'll be speaking at the inaugural UpdateConf in Brighton, UK on September 5th.

As part of the conference, I'm also giving a two-day intensive workshop on OpenGL ES. The workshop assumes no prior experience with graphics program, but will be fairly fast-paced and will cover a lot of ground because there's a lot of new ground to cover. I'll be going over the new GLKit and the incredible new OpenGL debugger as well as the updated OpenGL ES Instruments templates.

 Not interested in OpenGL ES? There are four other two-day workshops going on with some really awesome instructors. Marcus Zarra is doing a two-day workshop on Core Data, Drew McCormack is doing a workshop on Core Animation, Sarah Parmenter is doing one on iOS UI design, and Remy Sharp is doing one on HTML5 for mobile. The worst thing about giving this workshop is that doing so will prevent me from going to one of the other great workshops.

The conference proper has a number of speakers, including the workshop instructors and it's really shaping up to to be quite a conference, and also quite a steal at £99 for early bird tickets to the conference (the workshops are extra). You can sign up for the conference on EventBrite.

 Hope to see you there!