iphone: start adding toolchain elements for iphone
authorJeremy Lainé <jeremy.laine@m4x.org>
Fri, 17 Apr 2009 10:10:29 +0000 (10:10 +0000)
committerJeremy Lainé <jeremy.laine@m4x.org>
Fri, 17 Apr 2009 10:10:29 +0000 (10:10 +0000)
* iphone-sources: script to download and extract rootfs / SDKs
* iphone-sources.manifest: config file describing a firmware version
* iphone-rootfs.bb: stage libraries from firmware image
* iphone-sdks.bb: stage headers from SDKS

recipes/iphone/iphone-rootfs_2.2.1.bb [new file with mode: 0644]
recipes/iphone/iphone-sdks_2.2.1.bb [new file with mode: 0644]
recipes/iphone/iphone-sources [new file with mode: 0755]
recipes/iphone/iphone-sources.manifest [new file with mode: 0644]

diff --git a/recipes/iphone/iphone-rootfs_2.2.1.bb b/recipes/iphone/iphone-rootfs_2.2.1.bb
new file mode 100644 (file)
index 0000000..2978ab5
--- /dev/null
@@ -0,0 +1,23 @@
+DESCRIPTION = "Staging iPhone root filesystem"
+DEPENDS = "apple-csu iphone-sdks"
+PROVIDES = "virtual/${TARGET_PREFIX}libc-for-gcc virtual/libc virtual/libiconv virtual/libintl libsegfault"
+
+INHIBIT_DEFAULT_DEPS = "1"
+EXCLUDE_FROM_SHLIBS = "1"
+
+# note: see iphone-sources script to get/generate the tarballs
+SRC_URI = "file://iphone-rootfs-${PV}.tar.bz2"
+
+FILES_${PN} += "/System ${layout_libdir}/*"
+
+do_compile () {
+       :
+}
+
+do_stage () {
+       cp -apR ${S}/usr/lib/* ${STAGING_LIBDIR}/
+       cp -apR ${S}/System ${STAGING_DIR_TARGET}/
+       rm -rf ${STAGING_DIR_TARGET}/System/Library/Fonts
+       rm -rf ${STAGING_DIR_TARGET}/System/Library/Audio
+       rm -rf ${STAGING_DIR_TARGET}/System/Library/Caches
+}
diff --git a/recipes/iphone/iphone-sdks_2.2.1.bb b/recipes/iphone/iphone-sdks_2.2.1.bb
new file mode 100644 (file)
index 0000000..d355ccc
--- /dev/null
@@ -0,0 +1,201 @@
+DESCRIPTION = "iPhone development headers"
+SECTION = "libs"
+PROVIDES = "linux-libc-headers"
+
+INHIBIT_DEFAULT_DEPS = "1"
+
+# note: see iphone-sources script to get/generate the tarballs
+SRC_URI = "\
+       file://iphone-sdks-${PV}.tar.bz2 \
+       \
+       file://cctools-667.8.0.tar.gz \
+       file://CF-476.14.tar.gz \
+       file://configd-210.tar.gz \
+       file://DirectoryService-514.23.tar.gz \
+       file://DiskArbitration-183.tar.gz \
+       file://IOCDStorageFamily-39.tar.gz \
+       file://IODVDStorageFamily-26.tar.gz \
+       file://IOGraphics-193.2.tar.gz \
+       file://IOHIDFamily-258.3.tar.gz \
+       file://IOKitUser-388.2.1.tar.gz \
+       file://IOStorageFamily-88.tar.gz \
+       file://JavaScriptCore-466.1.tar.gz \
+       file://launchd-258.1.tar.gz \
+       file://Libc-498.tar.gz \
+       file://libsecurity_authorization-32564.tar.gz \
+       file://libsecurity_cdsa_client-32432.tar.gz \
+       file://libsecurity_cdsa_utilities-33506.tar.gz \
+       file://libsecurity_cms-32521.tar.gz \
+       file://libsecurity_codesigning-33803.tar.gz \
+       file://libsecurity_cssm-32993.tar.gz \
+       file://libsecurityd-33470.tar.gz \
+       file://libsecurity_keychain-34101.tar.gz \
+       file://libsecurity_mds-32820.tar.gz \
+       file://libsecurity_ssl-32463.tar.gz \
+       file://libsecurity_utilities-32820.tar.gz \
+       file://WebCore-351.9.tar.gz \
+       file://xnu-1228.3.13.tar.gz \
+       file://xnu-1228.7.58.tar.gz \
+"
+DARWIN_SOURCES_DIR = "${WORKDIR}"
+IPHONE_SDK_INC = "${S}/iPhoneOS${PV}.sdk/usr/include"
+IPHONE_SDK_LIBS = "${S}/iPhoneOS${PV}.sdk/System/Library/Frameworks"
+LEOPARD_SDK_INC = "${S}/MacOSX10.5.sdk/usr/include"
+LEOPARD_SDK_LIBS = "${S}/MacOSX10.5.sdk/System/Library/Frameworks"
+
+do_compile() {
+    find ${WORKDIR} ! -path "${S}/*" -print0 | xargs -0 chmod u+w 
+}
+
+# Follows the build routine for the toolchain described by saurik here:
+# www.saurik.com/id/4
+do_stage() {
+       install -d ${STAGING_INCDIR}
+
+       echo "Leopard"
+       cd ${STAGING_INCDIR}
+       rm -f System
+       ln -sf . System
+       cp -R -pf "${LEOPARD_SDK_INC}"/* ${STAGING_INCDIR}
+       cp -R -pf "${IPHONE_SDK_INC}"/* ${STAGING_INCDIR}
+       cp -R -pf "${DARWIN_SOURCES_DIR}"/xnu-1228.7.58/osfmk/* .
+       cp -R -pf "${DARWIN_SOURCES_DIR}"/xnu-1228.7.58/bsd/* . 
+
+       echo "mach"
+       cp -R -pf "${DARWIN_SOURCES_DIR}"/cctools-*/include/mach .
+       cp -R -pf "${DARWIN_SOURCES_DIR}"/cctools-*/include/mach-o .
+       cp -R -pf "${IPHONE_SDK_INC}"/mach-o/dyld.h mach-o
+
+       cp -R -pf "${LEOPARD_SDK_INC}"/mach/machine mach
+       cp -R -pf "${LEOPARD_SDK_INC}"/mach/machine.h mach
+       cp -R -pf "${LEOPARD_SDK_INC}"/machine .
+       cp -R -pf "${IPHONE_SDK_INC}"/machine .
+
+       cp -R -pf "${IPHONE_SDK_INC}"/sys/cdefs.h sys
+       cp -R -pf "${LEOPARD_SDK_INC}"/sys/dtrace.h sys
+
+       cp -R -pf "${LEOPARD_SDK_LIBS}"/Kernel.framework/Versions/A/Headers/machine/disklabel.h machine
+       cp -R -pf "${DARWIN_SOURCES_DIR}"/configd-*/dnsinfo/dnsinfo.h .
+       cp -R -p "${DARWIN_SOURCES_DIR}"/Libc-*/include/kvm.h .
+       cp -R -p "${DARWIN_SOURCES_DIR}"/launchd-*/launchd/src/*.h .
+
+       cp -R -p i386/disklabel.h arm
+       cp -R -p mach/i386/machine_types.defs mach/arm
+
+       mkdir -p Kernel
+       echo "libsa"
+       cp -R -p "${DARWIN_SOURCES_DIR}"/xnu-1228.3.13/libsa/libsa Kernel
+
+       mkdir -p Security
+       echo "libsecurity"
+       cp -R -p "${DARWIN_SOURCES_DIR}"/libsecurity_authorization-*/lib/*.h Security
+       cp -R -p "${DARWIN_SOURCES_DIR}"/libsecurity_cdsa_client-*/lib/*.h Security
+       cp -R -p "${DARWIN_SOURCES_DIR}"/libsecurity_cdsa_utilities-*/lib/*.h Security
+       cp -R -p "${DARWIN_SOURCES_DIR}"/libsecurity_cms-*/lib/*.h Security
+       cp -R -p "${DARWIN_SOURCES_DIR}"/libsecurity_codesigning-*/lib/*.h Security
+       cp -R -p "${DARWIN_SOURCES_DIR}"/libsecurity_cssm-*/lib/*.h Security
+       cp -R -p "${DARWIN_SOURCES_DIR}"/libsecurity_keychain-*/lib/*.h Security
+       cp -R -p "${DARWIN_SOURCES_DIR}"/libsecurity_mds-*/lib/*.h Security
+       cp -R -p "${DARWIN_SOURCES_DIR}"/libsecurity_ssl-*/lib/*.h Security
+       cp -R -p "${DARWIN_SOURCES_DIR}"/libsecurity_utilities-*/lib/*.h Security
+       cp -R -p "${DARWIN_SOURCES_DIR}"/libsecurityd-*/lib/*.h Security
+
+       mkdir -p DiskArbitration
+       echo "DiskArbitration"
+       cp -R -p "${DARWIN_SOURCES_DIR}"/DiskArbitration-*/DiskArbitration/*.h DiskArbitration
+
+       echo "iokit"
+       cp -R -p "${DARWIN_SOURCES_DIR}"/xnu-*/iokit/IOKit .
+       cp -R -p "${DARWIN_SOURCES_DIR}"/IOKitUser-*/*.h IOKit
+
+       cp -R -p "${DARWIN_SOURCES_DIR}"/IOGraphics-*/IOGraphicsFamily/IOKit/graphics IOKit
+       cp -R -p "${DARWIN_SOURCES_DIR}"/IOHIDFamily-*/IOHIDSystem/IOKit/hidsystem IOKit
+
+       for proj in kext ps pwr_mgt; do
+               mkdir -p IOKit/"${proj}"
+               cp -R -p "${DARWIN_SOURCES_DIR}"/IOKitUser-*/"${proj}".subproj/*.h IOKit/"${proj}"
+       done
+    
+       ln -sf IOKit/kext/bootfiles.h .
+
+       mkdir -p IOKit/storage
+       cp -R -p "${DARWIN_SOURCES_DIR}"/IOStorageFamily-*/*.h IOKit/storage
+       cp -R -p "${DARWIN_SOURCES_DIR}"/IOCDStorageFamily-*/*.h IOKit/storage
+       cp -R -p "${DARWIN_SOURCES_DIR}"/IODVDStorageFamily-*/*.h IOKit/storage
+
+       mkdir -p DirectoryService
+       cp -R -p "${DARWIN_SOURCES_DIR}"/DirectoryService-*/APIFramework/*.h DirectoryService
+
+       mkdir -p DirectoryServiceCore
+       cp -R -p "${DARWIN_SOURCES_DIR}"/DirectoryService-*/CoreFramework/Private/*.h DirectoryServiceCore
+       cp -R -p "${DARWIN_SOURCES_DIR}"/DirectoryService-*/CoreFramework/Public/*.h DirectoryServiceCore 
+
+       mkdir -p SystemConfiguration
+       echo "configd"
+       cp -R -p "${DARWIN_SOURCES_DIR}"/configd-*/SystemConfiguration.fproj/*.h SystemConfiguration
+
+       echo "CoreFoundation"
+       mkdir -p CoreFoundation
+       cp -R -p "${LEOPARD_SDK_LIBS}"/CoreFoundation.framework/Versions/A/Headers/* CoreFoundation
+       cp -R -pf "${DARWIN_SOURCES_DIR}"/CF-*/*.h CoreFoundation
+       cp -R -pf "${IPHONE_SDK_LIBS}"/CoreFoundation.framework/Headers/* CoreFoundation
+
+       for framework in AudioToolbox AudioUnit CoreAudio QuartzCore Foundation; do
+               echo $framework
+               mkdir -p $framework
+               cp -R -p "${LEOPARD_SDK_LIBS}"/"${framework}".framework/Versions/?/Headers/* "${framework}"
+               cp -R -pf "${IPHONE_SDK_LIBS}"/"${framework}".framework/Headers/* "${framework}"
+       done
+
+       for framework in UIKit AddressBook CoreLocation; do
+               echo $framework
+               mkdir -p $framework
+               cp -R -pf "${IPHONE_SDK_LIBS}"/"${framework}".framework/Headers/* "${framework}"
+       done
+
+       for framework in AppKit Cocoa CoreData CoreVideo JavaScriptCore OpenGL WebKit; do
+               echo $framework
+               mkdir -p $framework
+               cp -R -p "${LEOPARD_SDK_LIBS}"/"${framework}".framework/Versions/?/Headers/* $framework
+       done
+       
+       echo "Application Services"
+       mkdir -p ApplicationServices
+       cp -R -p "${LEOPARD_SDK_LIBS}"/ApplicationServices.framework/Versions/A/Headers/* ApplicationServices
+       for service in "${LEOPARD_SDK_LIBS}"/ApplicationServices.framework/Versions/A/Frameworks/*.framework; do
+               echo -e "\t$(basename $service .framework)"
+               mkdir -p "$(basename $service .framework)"
+               cp -R -p $service/Versions/A/Headers/* "$(basename $service .framework)"
+       done
+
+       echo "Core Services"
+       mkdir -p CoreServices
+       cp -R -p "${LEOPARD_SDK_LIBS}"/CoreServices.framework/Versions/A/Headers/* CoreServices
+       for service in "${LEOPARD_SDK_LIBS}"/CoreServices.framework/Versions/A/Frameworks/*.framework; do
+               mkdir -p "$(basename $service .framework)"
+               cp -R -p $service/Versions/A/Headers/* "$(basename $service .framework)"
+       done
+
+       echo "WebCore"
+       mkdir -p WebCore
+       cp -R -p "${DARWIN_SOURCES_DIR}"/WebCore-*/bindings/objc/*.h WebCore
+       cp -R -p "${DARWIN_SOURCES_DIR}"/WebCore-*/bridge/mac/*.h WebCore 
+       for subdir in css dom editing history html loader page platform{,/{graphics,text}} rendering; do
+           cp -R -p "${DARWIN_SOURCES_DIR}"/WebCore-*/"${subdir}"/*.h WebCore
+       done
+
+       cp -R -p "${DARWIN_SOURCES_DIR}"/WebCore-*/css/CSSPropertyNames.in WebCore
+       (cd WebCore; perl "${DARWIN_SOURCES_DIR}"/WebCore-*/css/makeprop.pl)
+
+       mkdir -p kjs
+       cp -R -p "${DARWIN_SOURCES_DIR}"/JavaScriptCore-*/kjs/*.h kjs
+
+       mkdir -p wtf/unicode/icu
+       cp -R -p "${DARWIN_SOURCES_DIR}"/JavaScriptCore-*/wtf/*.h wtf
+       cp -R -p "${DARWIN_SOURCES_DIR}"/JavaScriptCore-*/wtf/unicode/*.h wtf/unicode
+       cp -R -p "${DARWIN_SOURCES_DIR}"/JavaScriptCore-*/wtf/unicode/icu/*.h wtf/unicode/icu
+
+       mkdir -p unicode
+       cp -R -p "${DARWIN_SOURCES_DIR}"/JavaScriptCore-*/icu/unicode/*.h unicode
+}
+
diff --git a/recipes/iphone/iphone-sources b/recipes/iphone/iphone-sources
new file mode 100755 (executable)
index 0000000..0081d4c
--- /dev/null
@@ -0,0 +1,413 @@
+#!/usr/bin/python
+#
+# Download Apple sources and prepare them for OpenEmbedded.
+#
+# You need the following on your system to run this script:
+#
+#  python
+#  python-beautifulsoup
+#  libssl-dev
+#  sudo (to mount HFS+ images)
+#
+# Version: 0.4
+
+import cookielib
+import os
+import re
+import subprocess
+import shutil
+import sys
+import urllib
+import urllib2
+import urlparse
+from BeautifulSoup import BeautifulSoup
+import Cookie
+try:
+    import xml.etree.cElementTree as ET
+except:
+    import cElementTree as ET
+
+DOWNLOAD_DIR = "downloads"
+MOUNT_DIR = "mnt"
+OUTPUT_DIR = "files"
+TEMP_DIR = "tmp"
+TOOLS_DIR = "tools"
+
+class CustomCookieHandler(urllib2.BaseHandler):
+    """
+    Custom handler for cookies, as for some reason HTTPCookieProcessor
+    has issues parsing the cookies received from Apple.
+    """
+    def __init__(self):
+        self.cookiejar = Cookie.SimpleCookie()
+
+    def http_request(self, request):
+        # add cookies
+        attrs = []
+        for key in self.cookiejar:
+            attrs.append( key + "=" + self.cookiejar[key].value)
+        if len(attrs):
+            if not request.has_header("Cookie"):
+                request.add_unredirected_header(
+                    "Cookie", "; ".join(attrs))
+        return request
+
+    def http_response(self, request, response):
+        # store cookies
+        cookie = response.info().getheader('set-cookie')
+        if cookie:
+            self.cookiejar.load(cookie)
+
+        return response
+
+    https_request = http_request
+    https_response = http_response
+
+def plist_value(e):
+    """
+    Convert an XML element into its python representation.
+    """
+    if e.tag == "integer":
+        return int(e.text)
+    elif e.tag == "string":
+        return e.text
+    elif e.tag == "array":
+        return [ plist_value(c) for c in e.getchildren() ]
+    elif e.tag == "dict":
+        key = None
+        val = {}
+        for c in e.getchildren():
+            if c.tag == "key":
+                key = c.text
+            else:
+                val[key] = plist_value(c)
+        return val
+    else:
+        raise Exception("Could not parse dict entry %s" % e)
+
+def plist_to_hash(plist_string):
+    """
+    Convert the contents of an Apple .plist file to a hash.
+    """
+    return plist_value(ET.fromstring(plist_string).find('dict'))
+
+def extract_pkg(pkg, dest):
+    print "Extracting package %s to %s" % (pkg, dest)
+    if os.system("cd %s && xar -xf %s Payload && zcat Payload | cpio -id && rm Payload" % (dest, os.path.abspath(pkg))):
+        raise Exception("Could not extract package")
+
+def mount_dmg(dmg, mnt):
+    """
+    Convert a DMG image to an HFS+ image then mount it.
+
+    NOTE: requires sudo access
+    """
+    img = os.path.join(TEMP_DIR, os.path.basename(dmg).replace(".dmg", ".img"))
+
+    # Check image is not mounted
+    if not os.path.exists(mnt):
+        os.mkdir(mnt)
+
+    # Convert image
+    if not os.path.exists(img):
+        print "Converting image %s to %s " % (dmg, img)
+       if os.system("%s -i %s -o %s" % (os.path.join(TOOLS_DIR, "dmg2img"), dmg, img)):
+            raise Exception("Could not convert image")
+
+    # Mount image
+    print "Mounting image %s on %s" % (img, mnt)
+    if os.system("sudo mount -t hfsplus -o loop %s %s" % (img, mnt)):
+        raise Exception("Could not mount image")
+
+    return img
+
+def umount_dmg(dmg, mnt):
+    """
+    Unmount a DMG image.
+
+    NOTE: requires sudo access
+    """
+    img = os.path.join(TEMP_DIR, os.path.basename(dmg).replace(".dmg", ".img"))
+    print "Unmounting %s" % (mnt)
+    if os.system("sudo umount %s" % mnt):
+        raise Exception("Could not unmount image")
+    os.remove(img)
+
+def pagetype(page):
+     return page.info().getheader('content-type').split(';')[0]
+
+def request(url, data=None):
+    """
+    Wrapper around urllibs2.urlopen to handle Apple's authentication form.
+    """
+    page = urllib2.urlopen(url, data)
+
+    if page.geturl() != url:
+        # check for connect form
+        soup = BeautifulSoup(page)
+        form = soup.find("form", {"name": "appleConnectForm"})
+        if form:
+            login_url = urlparse.urljoin(page.geturl(), form['action'])
+
+            # log in
+            print "* Logging into %s" % login_url
+            page = urllib2.urlopen(urllib2.Request(login_url, data=urllib.urlencode({
+                'theAccountName': apple_id,
+                'theAccountPW': apple_password, 
+                'theAuxValue': '',
+                '1.Continue.x': '1',
+                '1.Continue.y': '1'}),
+                headers={'Referer': page.geturl()}))
+
+    # follow refresh
+    while pagetype(page) == "text/html":
+        soup = BeautifulSoup(page)
+        meta = soup.find('meta', {'http-equiv': 'REFRESH'})
+        if not meta:
+            break
+
+        refresh_url = meta['content'].split(';',1)[1].split("=",1)[1]
+        print "* Following refresh to %s" % refresh_url
+        page = urllib2.urlopen(urllib2.Request(refresh_url, data=data, headers={'Referer': page.geturl()}))
+
+    return page
+
+def download(url, dest):
+    """
+    Download a file to a directory.
+    """
+    tempname = os.path.join(TEMP_DIR, os.path.basename(url)) + ".part"
+    filename = os.path.join(dest, os.path.basename(url))
+    if os.path.exists(filename):
+        print "* Not downloading %s" % url
+        return filename
+
+    print "* Downloading %s" % url
+    page = request(url)
+    size = page.info().getheader('content-length')
+    if pagetype(page) == "text/html":
+        raise Exception("We got an HTML page, download failed")
+
+    done = 0
+    output = open(tempname, 'wb')
+    sys.stdout.write("\r* Receiving..")
+    sys.stdout.flush()
+    while True:
+        data = page.read(100000)
+        if data:
+            done += len(data)
+            if size:
+                sys.stdout.write("\r* Received %i bytes (%f %%)" % (done, float(done * 100) / float(size)))
+            else:
+                sys.stdout.write("\r* Received %i bytes (unknown)" % done)
+            sys.stdout.flush()
+            output.write(data)
+        else:
+            sys.stdout.write("\n")
+            break
+    output.close()
+
+    shutil.move(tempname, filename)
+    return filename
+def build_tools(url, binaries):
+    """
+    Build DMG handling tools.
+    """
+    print "[ Building tools ]"
+    done = True
+    for bin in binaries:
+        tool = os.path.join(TOOLS_DIR, bin)
+        if not os.path.exists(tool):
+            print "* Tool %s is missing" % bin
+            done = False
+    
+    if done:
+        print "* Tools already built"
+        return
+
+    # download and extract tools
+    workdir = os.path.join(TEMP_DIR, "tools")
+    if os.path.exists(workdir):
+        shutil.rmtree(workdir)
+    os.mkdir(workdir)
+    tarball = download(url, TEMP_DIR)
+    if os.system("tar -C %s --strip-components=1 -xf %s" % (workdir, tarball)):
+        raise Exception("Could not extract tarball")
+
+    # build tools
+    if os.system("make -C %s" % workdir):
+        raise Exception("Could not build tools")
+    for bin in binaries:
+        shutil.move(os.path.join(workdir, bin), os.path.join(TOOLS_DIR, bin))
+
+    # cleanup
+    shutil.rmtree(workdir)
+    os.remove(tarball)
+   
+def get_darwin_sources(urls):
+    """
+    Download the source .tar.gz of Darwin opensource components.
+    """
+    print "[ Darwin sources ]"
+    for url in urls:
+        download(url, OUTPUT_DIR)
+
+def get_firmware_key(firmware_version, firmware_build):
+    """
+    Return the key for a given firmware by parsing the iPhone wiki.
+    """
+    page = urllib2.urlopen('http://www.theiphonewiki.com/wiki/index.php?title=VFDecrypt_Keys:_2.x')
+    soup = BeautifulSoup(page)
+
+    for title in soup.findAll('span', {'class': 'mw-headline'}):
+        v = title.contents[0].strip()
+        if v.startswith(firmware_version) and v.count("(Build %s)" % firmware_build):
+            p = title.parent.findNextSibling('p')
+            return p.contents[0].strip().upper()
+
+    return None
+
+def generate_iphone_rootfs(url, exclude):
+    """
+    Generate a .tar.bz2 for the iPhone rootfs.
+    """
+    print "[ iPhone rootfs ]"
+    ipsw = download(url, DOWNLOAD_DIR)
+
+    # Get image contents
+    plist = plist_to_hash(subprocess.Popen(["unzip", "-p", ipsw, "Restore.plist"], stdout=subprocess.PIPE).communicate()[0])
+
+    tarname = "iphone-rootfs-%s" % plist['ProductVersion']
+    workdir = os.path.join(TEMP_DIR, tarname)
+    tarball = os.path.join(OUTPUT_DIR, tarname + ".tar.bz2")
+    tartemp = os.path.join(TEMP_DIR, tarname + ".tar.bz2")
+    imagename = plist['SystemRestoreImages']['User']
+    image = os.path.join(TEMP_DIR, imagename)
+
+    if os.path.exists(tarball):
+        print "Skipping %s" % tarball
+        return
+
+    for i in ['ProductType', 'ProductVersion', 'ProductBuildVersion']:
+        print "\t%s: %s" % (i, plist[i]) 
+
+    # Get firmware key
+    print "Fetching decryption key for %s" % image 
+    key = get_firmware_key(plist['ProductVersion'], plist['ProductBuildVersion'])
+    if not key:
+        raise Exception("Could not get decryption key for firmware")
+    print "Got decryption key %s" % key
+
+    # Get dmg
+    if not os.path.exists(image):
+        # Extract
+        print "Extracting %s" % image
+        os.system("unzip %s %s -d %s" % (ipsw, imagename, TEMP_DIR))
+
+        # Decrypt
+        temp = image + ".decrypted"
+        if os.system("%s -k %s -i %s -o %s" % (os.path.join(TOOLS_DIR, "vfdecrypt"), key, image, temp)):
+            raise Exception("Could not decrypt image")
+        os.remove(image)
+        os.rename(temp, image)
+
+    # Generate tarball
+    os.mkdir(workdir)
+    mount_dmg(image, workdir)
+
+    print "Generating %s" % tarball
+    excludes = " ".join([ "--exclude %s/%s" % (tarname, x) for x in exclude ])
+    if os.system("tar %s --hard-dereference -C %s -cjf %s %s" % (excludes, TEMP_DIR, tartemp, tarname)):
+        raise Exception("Could not create tarball")
+    shutil.move(tartemp, tarball)
+
+    umount_dmg(image, workdir)
+    os.rmdir(workdir)
+    os.remove(image)
+    return tarball
+
+def generate_iphone_sdk(url, iphone_version, macosx_version):
+    """
+    Generate a .tar.bz2 for the iPhone SDKs.
+    """
+    print "[ iPhone SDKs ]"
+    #request('http://developer.apple.com/iphone/login.action')
+    image = download(url, DOWNLOAD_DIR)
+
+    tarname = "iphone-sdks-%s" % iphone_version
+    workdir = os.path.join(TEMP_DIR, tarname)
+    tarball = os.path.join(OUTPUT_DIR, tarname + ".tar.bz2")
+    tartemp = os.path.join(TEMP_DIR, tarname + ".tar.bz2")
+
+    if os.path.exists(tarball):
+        print "Skipping %s" % tarball
+        return
+
+    # Cleanup
+    if os.path.exists(workdir):
+        shutil.rmtree(workdir)
+
+    # Mount image
+    mount_dmg(image, MOUNT_DIR)
+    os.mkdir(workdir)
+
+    # Extract iPhone stuff
+    sdk = "iPhoneOS%s.sdk" % iphone_version
+    tmpdir = os.path.join(TEMP_DIR, sdk)
+    os.mkdir(tmpdir)
+    extract_pkg("%s/Packages/iPhoneSDKHeadersAndLibs.pkg" % MOUNT_DIR, tmpdir)
+    shutil.move("%s/Platforms/iPhoneOS.platform/Developer/SDKs/%s" % (tmpdir, sdk), os.path.join(workdir, sdk))
+    shutil.rmtree(tmpdir)
+
+    # Extract MacOS stuff
+    sdk = "MacOSX%s.sdk" % macosx_version
+    tmpdir = os.path.join(TEMP_DIR, sdk)
+    os.mkdir(tmpdir)
+    extract_pkg("%s/Packages/MacOSX%s.pkg" % (MOUNT_DIR, macosx_version), tmpdir)
+    shutil.move("%s/SDKs/%s" % (tmpdir, sdk), os.path.join(workdir, sdk))
+    shutil.rmtree(tmpdir)
+
+    # Unmount image
+    umount_dmg(image, MOUNT_DIR)
+
+    print "Generating %s" % tarball
+    if os.system("tar -C %s -cjf %s %s" % (TEMP_DIR, tartemp, tarname)):
+        raise Exception("Could not create tarball")
+    shutil.move(tartemp, tarball)
+    shutil.rmtree(workdir)
+
+    return tarball
+
+if __name__ == "__main__":
+    # Check arguments
+    if len(sys.argv) < 4:
+        print """Usage: iphone-sources <manifest> <apple_id> <apple_password>"""
+        sys.exit(1)
+    cmd = sys.argv[1]
+
+    # Initialise directories
+    for d in [DOWNLOAD_DIR, OUTPUT_DIR, MOUNT_DIR, TEMP_DIR, TOOLS_DIR]:
+        if not os.path.exists(d):
+            print "Creating %s" % d
+            os.mkdir(d)
+
+    # Read manifest
+    manifest = eval(file(sys.argv[1]).read())
+    apple_id = sys.argv[2]
+    apple_password = sys.argv[3]
+
+    # Register cookies
+    opener = urllib2.build_opener(CustomCookieHandler())
+    urllib2.install_opener(opener)
+
+    # Perform all build steps
+    build_tools(manifest['tools']['url'], manifest['tools']['binaries'])
+    get_darwin_sources(manifest['sources'])
+    generate_iphone_rootfs(manifest['firmware']['url'], manifest['firmware']['exclude'])
+    generate_iphone_sdk(manifest['sdk']['url'], manifest['firmware']['version'], manifest['macosx']['version'])
+
+    # Cleanup
+    for d in [MOUNT_DIR, TEMP_DIR]:
+        os.rmdir(d)
+
diff --git a/recipes/iphone/iphone-sources.manifest b/recipes/iphone/iphone-sources.manifest
new file mode 100644 (file)
index 0000000..d9e27d2
--- /dev/null
@@ -0,0 +1,47 @@
+{
+    'tools': {
+        'binaries': ['dmg2img', 'vfdecrypt'],
+        'url': 'http://vu1tur.eu.org/tools/download.pl?dmg2img-1.3.tar.gz',
+    },
+    'macosx': {
+        'version': '10.5',
+    },
+    'sdk': {
+        'url': 'http://developer.apple.com/iphone/download.action?path=/iphone/iphone_sdk_for_iphone_os_2.2.1__9m2621a__final/iphone_sdk_for_iphone_os_2.2.19m2621afinal.dmg',
+    },
+    'firmware': {
+        'version': '2.2.1',
+        'url': 'http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-5828.20090127.aQLi8/iPhone1,2_2.2.1_5H11_Restore.ipsw',
+        'exclude': ['.fseventsd', '.HFS', 'etc', 'private', 'var', 'tmp'],
+    },
+    'sources': [
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/cctools-667.8.0.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/libsecurity_authorization-32564.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/libsecurity_cdsa_client-32432.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/libsecurity_cdsa_utilities-33506.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/libsecurity_cms-32521.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/libsecurity_codesigning-33803.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/libsecurity_cssm-32993.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/libsecurity_keychain-34101.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/libsecurity_mds-32820.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/libsecurity_ssl-32463.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/libsecurity_utilities-32820.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/libsecurityd-33470.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/DiskArbitration-183.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/IOKitUser-388.2.1.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/IOGraphics-193.2.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/IOHIDFamily-258.3.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/IOStorageFamily-88.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/IOCDStorageFamily-39.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/IODVDStorageFamily-26.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/other/WebCore-351.9.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/CF-476.14.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/configd-210.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/xnu-1228.7.58.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/xnu-1228.3.13.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/Libc-498.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/launchd-258.1.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/apsl/DirectoryService-514.23.tar.gz',
+        'http://www.opensource.apple.com/darwinsource/tarballs/other/JavaScriptCore-466.1.tar.gz',
+    ],
+}