scanframework.py :  » Development » PyObjC » trunk » pyobjc » pyobjc-metadata » Lib » PyObjCMetaData » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » PyObjC 
PyObjC » trunk » pyobjc » pyobjc metadata » Lib » PyObjCMetaData » scanframework.py
"""
The actual metadata generator.

Missing features:
    * Sync with the RubyCocoa stuff
      - ignored_headers in the exceptions file
    * Support for class methods
    * The script should also work with non-system frameworks
    * Suppress argument nodes in the metadata when there isn't any metadata
      (e.g. we've determineted that there should be an exception but no
       exception is present) -> keeps the metadata nice and small when we
       guess incorrect.
    * Load the framework and iterate over all classes to look for methods
      with difficult signatures (for compatibility with the current meta
      data in PyObjC).

Cleanup:
    * Merge FrameworkScanner and FrameworkMetadata?
    * Can we avoid the PPC compile? This is needed to find four-character-codes,
      and these now slow down metadata generation for the majority of frameworks
      that don't use these :-( :-(

Testing:
    * Run this script on frameworks and check by hand if all required 
      information is present.
    * Also compare with PyObjC-trunk

Performance:
    * We're currently compiling and running an awful lot of proglets, that
      makes development easier but isn't very good for speed. As one shouldn't
      have to run the tool very often anyway and the problem is most glaring
      on big frameworks like Foundation, I don't think I'll refactor for this.
"""
import objc
import os
import optparse
import string
import pkg_resources
import StringIO

# Temporary definitions, to help find problematic API's
##objc._C_UNICHAR = 'T'
##objc._C_CHAR_AS_TEXT = 't'
##objc._C_CPPBOOL = 'Z'
##objc._C_CHAR_AS_INT = 'z'

try:
    set
except NameError:
    from sets import Set

try:
    sorted
except NameError:
    def sorted(seq):
        seq2 = list(seq)
        seq2.sort()
        return seq2

import pprint
import glob
import sys
from macholib.dyld import framework_find
from itertools import *
from textwrap import dedent
from itertools import imap
import logging 
import subprocess
import cPickle as pickle
import re

from PyObjCMetaData.tokenize_header import *
from PyObjCMetaData.et import *


gStructFieldNameRe = re.compile('"[^"]*"')

gBooleanAttributesDefaultingTrue=[
    'function_pointer_retained',
    'null_accepted',
]

gBooleanAttributesDefaultingFalse=[
    'nsstring',
    'already_retained',
    'already_cfretained',
    'class_method',
    'c_array_length_in_result',
    'c_array_delimited_by_null',
    'c_array_of_variable_length',
    'printf_format',
    'function_pointer',
    'magic_cookie',
]

gTypeModifierAttributes = [
    'c_array_length_in_arg',
    'c_array_of_fixed_length',
    'c_array_delimited_by_null',
    'c_array_length_in_result',
    'c_array_of_variable_length',
    'type_modifier',
    'sel_of_type',
]

gExceptionAttributes = dict(
    constant = [ 'type', 'type64', 'magic_cookie' ],

    struct = [ 'type', 'type64' ],

    string_constant = [ 'value', 'nsstring' ],

    retval = [ 
            'type', 'type64', 
            'already_retained',
            'already_cfretained',
            'numeric',
            'c_array_length_in_arg',
            'c_array_of_fixed_length',
            'c_array_delimited_by_null',
            'c_array_of_variable_length',
            'sel_of_type',
            'function_pointer',
            'function_pointer_retained',
        ],
    arg = [ 
            'type', 'type64', 
            'type_modifier', 
            'null_accepted', 
            'already_retained',
            'already_cfretained',
            'numeric',
            'c_array_length_in_arg',
            'c_array_of_fixed_length',
            'c_array_delimited_by_null',
            'c_array_length_in_result',
            'printf_format',
            'c_array_of_variable_length',
            'sel_of_type',
            'function_pointer',
            'function_pointer_retained',
        ],

    cftype = [ 'gettypeid_func', 'tollfree', ],

    method = [ 'suggestion', 'c_array_delimited_by_null'],
)
gExceptionAttributes[None] = [ 'ignore', 'comment' ]


# XML header for the metadata files, needed because ElementTree won't write
# and XML declaration or doctype
HEADER='''\
<?xml version='1.0'?>
<!DOCTYPE signatures SYSTEM "file://localhost/System/Library/DTDs/BridgeSupport.dtd">'''


COMPILE_FILE='''\
#import <Foundation/Foundation.h> /* to pick up std definitions */
#import <AppKit/AppKit.h> /* Because Automator framework sucks... */
%(includes)s
#include <stdio.h>

%(globalDefinitions)s

int main(void)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
%(mainBody)s

    [pool release];
    return 0;
}
'''

IVAR_TYPE_DEFINITIONS='''\
#import <objc/objc-class.h>

@interface _SCANFRAMEWORK : NSObject
{
    %(ivarType)s ivar;
}
@end
@implementation _SCANFRAMEWORK
@end

#ifndef MAC_OS_X_VERSION_10_5
/* ^^^ What's actually needed is some configure-like logic to detect if
 * ivar_getTypeEncoding is available.
 */
#define ivar_getTypeEncoding(ivar) ((ivar)->ivar_type)
#endif
'''

IVAR_TYPE_MAIN='''\
    printf("%s\\n", ivar_getTypeEncoding(class_getInstanceVariable([_SCANFRAMEWORK class], "ivar")));
'''


#
# Some code for printing the values for enums and simple defines. The code works
# without knowing the type of the to-be-printed value beforehand.
#

ENUM_DEFINITIONS='''\
#include <objc/objc-runtime.h>
#include <ctype.h>

#ifndef _C_LNG_LNG
#define _C_LNG_LNG 'q'
#endif

#ifndef _C_ULNG_LNG
#define _C_ULNG_LNG 'Q'
#endif
'''

# NOTE: the circumspect way of printing is needed to avoid compile-time errors.
ENUM_MAIN=r'''
    __typeof__(%(name)s) tmpval = %(name)s;

    const char* tp = @encode(__typeof__(tmpval));
    if (*tp == _C_ARY_B) {
        tp++;
        while (isdigit(*tp)) tp++;
        if (*tp == _C_CHR) {
            printf("%%c\n", _C_CHARPTR);
            printf("%%s\n", tmpval);
            return 0;
        } 
    }

    printf("%%s\n", @encode(__typeof__(tmpval)));
    switch (*@encode(__typeof__(tmpval))) {
    case _C_LNG_LNG: 
        printf("%%lld\n", *(long long*)&tmpval);
        break;
    case _C_LNG:
        printf("%%lld\n", (long long)*(long*)&tmpval);
        break;
    case _C_INT:
        printf("%%lld\n", (long long)*(int*)&tmpval);
        break;
    case _C_SHT:
        printf("%%lld\n", (long long)*(short*)&tmpval);
        break;
    case _C_CHR:
        printf("%%lld\n", (long long)*(char*)&tmpval);
        break;

    case _C_ULNG_LNG:
        printf("%%llu\n", *(unsigned long long*)&tmpval);
        break;
    case _C_ULNG:
        printf("%%lld\n", (unsigned long long)*(unsigned long*)&tmpval);
        break;
    case _C_UINT:
        printf("%%lld\n", (unsigned long long)*(unsigned int*)&tmpval);
        break;
    case _C_USHT:
        printf("%%lld\n", (unsigned long long)*(unsigned short*)&tmpval);
        break;
    case _C_UCHR:
        printf("%%lld\n", (unsigned long long)*(unsigned char*)&tmpval);
        break;

    case _C_DBL:
        printf("%%.17g", *(double*)&tmpval);
        break;

    case _C_FLT:
        printf("%%.17g", (double)*(float*)&tmpval);
        break;

    case _C_CHARPTR:
        printf("%%s\n", tmpval);
        break;

    case _C_ID:
        /* This is ugly as hell, but needed to get this code through the
         * compiler when the value isn't an object.
         */
        if ([*(NSObject**)&tmpval isKindOfClass:[NSString class]]) {
            printf("%%s\n", ([*(NSString**)&tmpval UTF8String]));
            break;
        }
        /* FALL THROUGH */

    default:
        if (strcmp(@encode(__typeof__(tmpval)),@encode( __typeof__(CFSTR("")))) == 0) {
            printf("%%s\n", ([*(NSString**)&tmpval UTF8String]));
            break;

        } else if (*@encode(__typeof__(tmpval)) == _C_ARY_B)  {
            char* eltype = @encode(__typeof__(tmpval));
            eltype += 1;
            while (isdigit(*eltype)) eltype++;
            if (*eltype == _C_CHR) {
                printf("%%s\n", *(char**)&tmpval);
            }
            break;
        }
        fprintf(stderr, "Sorry, don't know how to print %(name)s: %%s\n",
            @encode(__typeof__(tmpval)));
        return 1;
    }
    return 0;
'''

def simpleValue(value):
    try:
        v = int(value.strip(), 0)
        return True
    except ValueError:
        return False

def indentET(elem, level=0):
    """ Add whitespace to an elementtree to make it print nicely """

    i = "\n" + level*"  "
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + "  "
        for el in elem:
            indentET(el, level+1)
        if not el.tail or not el.tail.strip():
            el.tail = i
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = i

class cleanfile(file):
    def write(self, s):
        file.write(self, cleanup_text(s))

    def writelines(self, lines):
        file.writelines(self, imap(cleanup_text, lines))

class typedict(object):
    def __init__(self):
        self.headers = set()
        self.dct = {'%% HEADERS %%': self.headers}
        
    def update(self, dct):
        if not isinstance(dct, dict):
            dct = dict(dct)
        for k,v in dct.iteritems():
            self[k] = v


    def __len__(self):
        return len(self.dct)

    def __iter__(self):
        return iter(self.dct)

    def __contains__(self, key):
        return normalize_type(key) in self.dct

    def addHeader(self, header):
        if header in self.headers:
            return False
        self.headers.add(header)
        return True

    def __setitem__(self, key, item):
        # allow updates from a type hint file
        if key == '%% HEADERS %%':
            try:
                self.dct[key].update(item)
            except KeyError:
                self.dct[key] = set(item)
            return
        key = normalize_type(key)
        self.dct[key] = item
        #print '[type] %s -> %s' % (key, item)

    def __getitem__(self, key):
        key = normalize_type(key)
        return self.dct[key]

    def get(self, key, default=None):
        key = normalize_type(key)
        return self.dct.get(key, default)


def update_fallback(env, framework):
    frameworks = os.path.join(framework, 'Frameworks')
    if os.path.exists(frameworks):
        fallback = env.get('DYLD_FALLBACK_FRAMEWORK_PATH')
        if fallback is None:
            fallback = frameworks
        else:
            fallback += ':' + frameworks
        env['DYLD_FALLBACK_FRAMEWORK_PATH'] = fallback

def framework_header(f, known={}, env=dict(os.environ)):
    headers = known.get(f)
    if headers is not None:
        return headers
    dylib = locate_framework(f, env=env)
    framework = os.path.dirname(dylib)
    update_fallback(env, framework)
    headers = known[f] = os.path.join(framework, 'Headers')
    return headers

def link_flag_for_framework(name):
    try:
        framework_find(name)
        return name

    except ValueError:
        subpath = 'Frameworks/%s.framework'%(name,)
        for basedir in '/Library/Frameworks', '/System/Library/Frameworks':
            for dn in os.listdir(basedir):
                possibleMatch = os.path.join(basedir, dn, subpath)
                if os.path.exists(possibleMatch):
                    return os.path.splitext(dn)[0]
        raise

def locate_framework(name, **kwds):
    try:
        return framework_find(name, **kwds)
    except ValueError:
        # Framework wasn't found directly, it might be a subframework
        if name.endswith('.framework'):
            subpath = 'Frameworks/%s'%(name,)
        else:
            subpath = 'Frameworks/%s.framework'%(name,)

        for basedir in '/Library/Frameworks', '/System/Library/Frameworks':
            for dn in os.listdir(basedir):
                possibleMatch = os.path.join(basedir, dn, subpath)
                if os.path.exists(possibleMatch):
                    return os.path.join(possibleMatch, os.path.splitext(name)[0])

        # Not found after all.
        raise




def locate_header(f):
    framework, header = os.path.split(f)
    return os.path.join(framework_header(framework + '.framework'), header)
    
class Dependency(object):
    def __init__(self, header):
        self.header = header

    def infoTuple(self):
        return (self.header,)

    def __repr__(self):
        return '%s%r' % (type(self).__name__, self.infoTuple())



    

class FrameworkScanner(object):
    def __init__(self, additional=None):
        self._scanner = None
        if additional is not None:
            self._additionalHeaders = additional
        else:
            self._additionalHeaders = []

    def scanframework(self, name, sub=None):
        if sub is None:
            sub = name
        start = '%s/%s.h' % (name, sub)
        startfile = locate_header(start)
        startframework = os.path.join(os.path.dirname(locate_framework(name)), '')
        self.seen = seen = []
        scanners = []
        if os.path.exists(startfile):
            seen.append(start)
            scanners.append(self.scanfile(startfile))
            for item in self._additionalHeaders:
                headers = glob.glob(locate_header('%s/%s.h' % (name, item)))
                for header in headers:
                    seen.append(name + '/' + os.path.basename(header))
                    scanners.append(self.scanfile(header))

        else:
            headers = glob.glob(locate_header('%s/*.h' % (name,)))
            for header in headers:
                seen.append(name + '/' + os.path.basename(header))
                scanners.append(self.scanfile(header))
        while scanners:
            try:
                token = scanners[-1].next()
            except StopIteration:
                scanners.pop()

                if not scanners:
                    # We're done with all regular headers, check if we missed
                    # some optional headers:
                    all_headers = glob.glob(locate_header('%s/*.h' % (name,)))
                    for header in all_headers:
                        p = name + '/' + os.path.basename(header)
                        if p not in seen:
                            seen.append(p)
                            scanners.append(self.scanfile(header))

                continue
            if isinstance(token, (AngleImport, StringImport)):
                fn = token['import_file']
                if fn in seen:
                    pass
                else:
                    try:
                        header = locate_header(fn)
                    except ValueError:
                        pass
                    else:
                        seen.append(fn)
                        if header.startswith(startframework):
                            hfn = locate_header(fn)
                            if os.path.exists(hfn):
                                scanners.append(self.scanfile(hfn))
                        else:
                            yield Dependency(fn)
                continue
            yield token
    
    def scanfile(self, fn):
        if self._scanner is None:
            self._scanner = Scanner(LEXICON)
        def deadraise(string, i, j):
            print '-' * len(fn)
            print fn
            print '-' * len(fn)
            s = string[i:].split('\n',1)[0]
            print s
            print
            import pdb
            pdb.Pdb().set_trace()

        if fn.lower().endswith('obsolete.h'):
            return iter(())

        logging.info("scanning %s", fn)
        return self._scanner.iterscan(file(fn).read(), dead=deadraise)

def contains_instances_of(iterator, types):
    for i in iterator:
        if isinstance(i, types):
            return True
    return False


def normalize_typename(value):
    value = value.strip().replace('\t', ' ')
    while '  ' in value:
        value = value.replace('  ', ' ')
    value = value.replace(' *', '*')

    if value.endswith('*'):
        try:
            objc.lookUpClass(value[:-1])
            return 'id'
        except objc.error:
            pass
    return value




class FrameworkMetadata (object):

    def __init__(self, framework, types=None, types64=None, dependencies=None):
        self.link_framework = link_flag_for_framework(framework)
        assert self.link_framework is not None

        if framework not in  ('Foundation', 'AppKit'):
            self.CFLAGS='-pipe -framework %s -framework AppKit -framework Foundation'%(self.link_framework,)
        else:
            self.CFLAGS='-framework %s'%(framework,)

        if sys.byteorder == 'little':
            self.CFLAGS64='-arch x86_64 '
        else:
            self.CFLAGS64='-arch ppc64 '

        self.framework = framework

        if types is None:
            self.types = typedict()
        else:
            self.types = types

        if types64 is None:
            self.types64 = typedict()
        else:
            self.types64 = types

        if dependencies is None:
            self.dependencies = set()
        else:
            self.dependencies = dependencies

        self.globthings = []
        self.enums = []
        self.strvals = []
        self.imports = []
        self.structs = []
        self.plain_structs = {}
        self.opaque_structs = {}
        self.opaque_pointers = []
        self.cftypes = []
        self.functions = []
        self.inline_functions = []
        self.informal_protocols = []
        self.classes = {}
        self.null_const = []
        self.static_inlines = []

        self.special_type_encodings = set()
        self.special_type_encodings.add(objc._C_BOOL)
        self.special_type_encodings.add(objc._C_PTR+objc._C_BOOL)

        if hasattr(objc, '_C_UNICHAR'):
            self.special_type_encodings.add(objc._C_UNICHAR)
            self.special_type_encodings.add(objc._C_PTR+objc._C_UNICHAR)
            self.special_type_encodings.add(objc._C_PTR+objc._C_PTR+objc._C_UNICHAR)
            self.special_type_encodings.add(objc._C_CONST+objc._C_PTR+objc._C_UNICHAR)
        else:
            objc._C_UNICHAR = self.encodedType('unichar')[0]

        if hasattr(objc, '_C_CHAR_AS_TEXT'):
            self.special_type_encodings.add(objc._C_CHAR_AS_TEXT)
            self.special_type_encodings.add(objc._C_PTR+objc._C_CHAR_AS_TEXT)
            self.special_type_encodings.add(objc._C_PTR+objc._C_PTR+objc._C_CHAR_AS_TEXT)
            self.special_type_encodings.add(objc._C_CONST+objc._C_PTR+objc._C_CHAR_AS_TEXT)

        if hasattr(objc, '_C_CHAR_AS_INT'):
            self.special_type_encodings.add(objc._C_CHAR_AS_INT)
            self.special_type_encodings.add(objc._C_PTR+objc._C_CHAR_AS_INT)
            self.special_type_encodings.add(objc._C_PTR+objc._C_PTR+objc._C_CHAR_AS_INT)
            self.special_type_encodings.add(objc._C_CONST+objc._C_PTR+objc._C_CHAR_AS_INT)

        if hasattr(objc, '_C_CPPBOOL'):
            self.special_type_encodings.add(objc._C_CPPBOOL)
            self.special_type_encodings.add(objc._C_PTR+objc._C_CPPBOOL)
            self.special_type_encodings.add(objc._C_PTR+objc._C_PTR+objc._C_CPPBOOL)
            self.special_type_encodings.add(objc._C_CONST+objc._C_PTR+objc._C_CPPBOOL)

        # Due to a bug in the compiler 'unsigned char*' gets @encode-d into
        # the wrong value, try to compensate for that.
        self.special_type_encodings.add(objc._C_PTR+objc._C_UCHR)

        self.cfstringtype = self.encodedType('CFStringRef')[0]
        self.unichartype = self.encodedType('UniChar')[0]

        self.exceptions = None

        # Type information from dependencies:
        self._dep_cftypes = []
        self._dep_opaque = []

        # This is rather jucky, for some reason webkit uses an #ifdef for
        # WebNSInteger and undefines this at the end of include files instead
        # of using typedefs.
        # This is a workaround to ensure that webkit will work with this tool.
        if os.uname()[2] < '9.':
            WebNSInteger='int'
            WebNSUInteger='unsigned int'
        else:
            WebNSInteger='NSInteger'
            WebNSUInteger='NSUInteger'

        self.types['WebNSInteger'], self.types64['WebNSInteger'] = self.encodedType(WebNSInteger)
        self.types['WebNSUInteger'], self.types64['WebNSUInteger'] = self.encodedType(WebNSUInteger)

        self.loadedMetaData = set()

        if framework != 'CoreFoundation':
            # Dependences aren't always explicit
            self.tryLoadMetaData('CoreFoundation')

        if framework == 'CoreVideo':
            # Pseudo framework with OpenGL related definitions
            self.tryLoadMetaData('CGL')
            self.tryLoadMetaData('CoreGraphics')

    def extractTypes(self, framework):
        """
        Extract type definitions from a framework. 

        This function only parses type definitions and updates the type table.
        """
        return

        if not self.types.addHeader(framework):
            return

        logging.info("extract types from: %s", framework)

        return;

        f = FrameworkMetadata(framework, 
                self.types, self.types64, self.dependencies)
        f.scan()

    def encodedType(self, ctype, _framework=None):
        ctype = normalize_typename(ctype)
        if not ctype:
            raise RuntimeError, "No type??"

        if ctype in self.types:
            # XXX: the types64 one isn't fully correct
            return self.types[ctype], self.types64.get(ctype, '')

        _ctype = ctype

        if ctype.startswith('in '):
            prefix = objc._C_IN
            ctype = ctype[3:]

        elif ctype.startswith('inout '):
            prefix = objc._C_INOUT
            ctype = ctype[6:]

        elif ctype.startswith('out '):
            prefix = objc._C_OUT
            ctype = ctype[4:]

        elif ctype.startswith('oneway '):
            prefix = objc._C_ONEWAY
            ctype = ctype[7:]

        else:
            prefix = ''

        data, data2 = self._compileAndRun(
                r'printf("%%s\n", @encode(%s));'%(ctype), additionalFramework=_framework)
        data = data.strip()
        data2 = data2.strip()

        # Strip const label to avoid confusion
        if data.startswith(objc._C_CONST):
            data = data[1:]
        if data2.startswith(objc._C_CONST):
            data2 = data2[1:]

        if data == '*':
            # Due to a bug in the objective-C compiler both 'char*' and 'unsigned char*'
            # get compiled into '*' (the later should be '^C').
            d, d2 = self._compileAndRun(
                    r'%s x; printf("%%s\n", @encode(*x));'%(ctype), additionalFramework=_framework)
            d = d.strip()
            d2 = d2.strip()
            if d.startswith(objc._C_CONST):
                d = d[1:]
            if d2.startswith(objc._C_CONST):
                d2 = d2[1:]
            if d == objc._C_UCHR:
                data = objc._C_PTR + objc._C_UCHR
            if d2 == objc._C_UCHR:
                data2 = objc._C_PTR + objc._C_UCHR

        # Make up a struct tag if a struct definition doesn't have one
        if data and data.startswith('{?'):
            if ' ' not in ctype:
                data = data[0] + ctype + data[2:]

        if data2 and data2.startswith('{?'):
            if ' ' not in ctype:
                data2 = data2[0] + ctype + data2[2:]


        if data:
            self.types[_ctype] = prefix + data
        if data2:
            self.types64[_ctype] = prefix + data2

        if data:
            return (prefix + data, prefix+data2)

        return None, None


    def exceptionNodes(self, cls, exceptions=None):
        if exceptions is None:
            exceptions = self.exceptions

            if exceptions is None:
                return

        for node in exceptions.findall(cls):
            yield node

    def find_exception(self, cls, name, child_tag=None, child_index=None, exceptions=None, nameattr='name', indexattr='index'):

        if exceptions is None:
            if self.exceptions is None:
                return

            exceptions = self.exceptions.getroot()

        for elem in exceptions.findall(cls):
            if elem.get(nameattr, None) == name:
                if child_tag is None:
                    return elem

                else:
                    if child_index is not None:
                        for child in elem:
                            if child.tag != child_tag:
                                continue
                            
                            if child.get(indexattr) == str(child_index):
                                return child
                    else:
                        for child in elem.getchildren():
                            if unicode(child.tag) == unicode(child_tag):
                                return child

        return None

    def haveFunction(self, name):
        for item in self.functions:
            if item[0] == name:
                return True
        return False

    def isStringyType(self, encoded, typestr):
        # Return True if the type is obviously textual.
        # Note that we can only deduce this for unicode characters/strings,
        # although I'm returning True for all char's as well.
        if typestr is None:
            return False

        typestr = normalize_type(typestr)
        if encoded in (
                objc._C_CHR, objc._C_CHARPTR, objc._C_PTR+objc._C_CHR,
                objc._C_PTR+objc._C_CHARPTR, 
                objc._C_PTR+objc._C_PTR+objc._C_CHR,
                ):
            if typestr in ('char', 'char*', 'char**'):
                return True
            return False

        if encoded != self.unichartype:
            return False

#        if typestr in ('unichar', 'UniChar', 'unichar*', 'UniChar*'):
#            return True
#
#        elif typestr in ('unichar**', 'UniChar**'):
#            return True

        return False


    def isCFType(self, encoded):
        # XXX: Need to load data for dependend frameworks as well.
        while encoded and encoded[0] in (
                objc._C_IN, objc._C_OUT, objc._C_INOUT, objc._C_CONST):
            encoded = encoded[1:]

        for nm, tp, tp64 in self.cftypes:
            if tp == encoded:
                return True

        for nm, tp, tp64 in self._dep_cftypes:
            if tp == encoded:
                return True

        return False

    def isOpaquePointerType(self, encoded):
        while encoded and encoded[0] in (
                objc._C_IN, objc._C_OUT, objc._C_INOUT, objc._C_CONST):
            encoded = encoded[1:]

        for name, tp, tp64 in self.opaque_pointers:
            if tp == encoded:
                return True

        for name, tp, tp64 in self._dep_opaque:
            if tp == encoded:
                return True

        return False


    def hasModifier(self, encoded):
        return encoded and encoded[0] in (
            objc._C_IN, objc._C_OUT, objc._C_INOUT) #, objc._C_CONST)

    def isPointerType(self, encoded):
        if encoded is None:
            return False

        # First strip type qualifiers:
        while encoded and encoded[0] in (
                objc._C_IN, objc._C_OUT, objc._C_INOUT, objc._C_CONST):
            encoded = encoded[1:]

        if encoded == objc._C_CHARPTR:
            return True

        if not encoded.startswith(objc._C_PTR):
            return False

        if self.isCFType(encoded) or self.isOpaquePointerType(encoded):
            return False

        return True
    
    def emitDependecyList(self, fp):
        for k in sorted(self.dependencies):
            print >>fp, k

    def emitMetaDataXML(self, fp):
        """
        Write the XML file with metadata information.
        """

        # Note: the code below needs to ensure that data will be written out
        # in a stable order to ensure that it is possible to compare the output
        # of two runs without special tools. I've chosen alphabetical order here.
        root = ET.Element('signatures', version='1.0')

        for dep in self.dependencies:
            path = locate_framework(dep)
            exc = self.find_exception('depends_on',  path, nameattr='path')
            if exc is not None and exc.get('ignore') == 'true':
                continue

            e = ET.SubElement(root, 'depends_on', path=path)
            self.copyExceptionData(e, exc)
            self.pruneComment(e)

        for name, encoding, encoding64 in sorted(self.structs):
            exc = self.find_exception('struct', name)
            if exc is not None and exc.get('ignore') == 'true':
                continue
        
            if exc is None and name.startswith('_'):
                continue

            e = ET.SubElement(root, 'struct', name=name, type=encoding)
            if encoding64:
                e.set('type64', encoding64)

            self.copyExceptionData(e, exc)
            self.pruneComment(e)


        for name, encoding, encoding64 in sorted(self.opaque_pointers):
            exc = self.find_exception('opaque', name)
            if exc is not None and exc.get('ignore') == 'true':
                continue

            if exc is None and name.startswith('_'):
                continue

            e = ET.SubElement(root, 'opaque', name=name, type=encoding)
            if encoding64:
                e.set('type64', encoding64)

            self.copyExceptionData(e, exc)
            self.pruneComment(e)

        for name, encoding, encoding64 in sorted(self.cftypes):
            exc = self.find_exception('cftype', name)
            if exc is not None and exc.get('ignore') == 'true':
                continue

            e = ET.SubElement(root, 'cftype', name=name, type=encoding)
            if encoding64:
                e.set('type64', encoding64)

            self.copyExceptionData(e, exc)
            self.pruneComment(e)

        lastName = None
        for name, type, type64 in sorted(self.globthings):

            if name == lastName:
                # Some frameworks offer multiple definitions for global thinks,
                # AddressBook is a prime example of this. This code avoids
                # duplicate definitions in the metadata.
                continue

            lastName = name

            exc = self.find_exception('constant', name)
            if exc is not None and exc.get('ignore') == 'true':
                continue

            e = ET.SubElement(root, 'constant', name=name, type=type)
            if type64 and type64 != type:
                e.set('type64', type64)

            self.copyExceptionData(e, exc)
            self.pruneComment(e)

        for name, value, value64, valuePPC in sorted(self.enums):
            exc = self.find_exception('enum', name)
            if exc is not None and exc.get('ignore') == 'true':
                continue

            if name.startswith('_'):
                # Ignore private names unless explicitly told otherwise
                if exc is None or exc.get('ignore') != 'false':
                    continue

            e = ET.SubElement(root, 'enum', name=name)

            if value and valuePPC and (value != valuePPC):
                e.set('be_value', valuePPC)
                e.set('le_value', value)

            elif value:
                e.set('value', value)

            if value64 and value64 != value:
                e.set('value64', value64)

            self.copyExceptionData(e, exc)
            self.pruneComment(e)

        for name, tp, value, value64 in sorted(self.strvals):
            exc = self.find_exception('string_constant', name)
            if exc is not None and exc.get('ignore') == 'true':
                continue

            # Maybe stuff the data into the text?
            e = ET.SubElement(root, 'string_constant', name=name)
            if value:
                if isinstance(value, str):
                    e.set('value', value.decode('utf-8'))
                else:
                    e.set('value', value)
            if value64 and value64 != value:
                if isinstance(value64, str):
                    e.set('value64', value64.decode('utf-8'))
                else:
                    e.set('value64', value64)

            if tp == objc._C_ID:
                e.set('nsstring', 'true')

            self.copyExceptionData(e, exc)
            self.pruneComment(e)

        for name in sorted(self.null_const):
            exc = self.find_exception('null_const', name)
            if exc is not None and exc.get('ignore') == 'true':
                continue

            e =  ET.SubElement(root, 'null_const', name=name)
            self.copyExceptionData(e, exc)
            self.pruneComment(e)


        for nm, (retType, retType64, returnMeta), arginfo, variadic in sorted(self.functions):
            exc = self.find_exception('function', nm)
            if exc is not None and exc.get('ignore') == 'true':
                continue

            func = ET.SubElement(root, 'function')

            if variadic:
                func.set('variadic', 'true')

            func.set('name', nm)
            self.copyExceptionData(func, exc)

            retexc = self.find_exception('function', nm, 'retval')

            if retexc is not None or retType != objc._C_VOID or returnMeta:
                e = ET.SubElement(func, 'retval', type=retType)
                if retType64 and retType64 != retType:
                    e.set('type64', retType64)

                for k, v in returnMeta.items():
                    e.set(k, v)

                self.copyExceptionData(e, retexc, copyChildren=True)
                self.pruneComment(e)

            for idx, arg in enumerate(arginfo):
                exc = self.find_exception('function', nm, 'arg', idx)
                e = ET.SubElement(func, 'arg')

                e.set('type', arg['encoded'])
                if arg['encoded64'] and arg['encoded64'] != arg['encoded']:
                    e.set('type64', arg['encoded'])

                if 'numeric' in arg:
                    e.set('numeric', arg['numeric'])

                self.copyExceptionData(e, exc, copyChildren=True)
                self.pruneComment(e)

        for nm, (retType, retType64), arginfo, variadic in sorted(self.inline_functions):
            exc = self.find_exception('function', nm)
            if exc is not None and exc.get('ignore') == 'true':
                continue

            func = ET.SubElement(root, 'function', inline='true')

            if variadic:
                func.set('variadic', 'true')

            func.set('name', nm)

            retexc = self.find_exception('function', nm, 'retval')

            if True: # retexc or (retType != objc._C_VOID):
                e = ET.SubElement(func, 'retval', type=retType)
                if retType64 and retType64 != retType:
                    e.set('type64', retType64)

                self.copyExceptionData(e, retexc, copyChildren=True)
                self.pruneComment(e)

            for idx, arg in enumerate(arginfo):
                exc = self.find_exception('function', nm, 'arg', idx)
                e = ET.SubElement(func, 'arg')

                e.set('type', arg['encoded'])
                if arg['encoded64'] and arg['encoded64'] != arg['encoded']:
                    e.set('type64', arg['encoded'])

                self.copyExceptionData(e, exc, copyChildren=True)
                self.pruneComment(e)

        for nm, methods in sorted(self.informal_protocols):
            protoexc = self.find_exception('informal_protocol', nm)
            if protoexc is not None and protoexc.get('ignore') == 'true':
                continue

            proto = ET.SubElement(root, 'informal_protocol')
            proto.set('name', nm)
            self.copyExceptionData(proto, protoexc)
            self.pruneComment(proto)

            for m in sorted(methods, key=lambda item: item['selector']):
                exc = self.find_exception('method', nm, m['selector'], nameattr='selector', exceptions=protoexc)
                if exc is not None and exc.get('ignore') == 'true': 
                    continue

                e = ET.SubElement(proto, 'method')
                for k, v in sorted(m.items()):
                    if isinstance(v, bool):
                        if v:
                            e.set(k, 'true')
                        else:
                            e.set(k, 'false')
                    else:
                        e.set(k, str(v))
                self.copyExceptionData(e, exc)
                self.pruneComment(e)

        for classname in sorted(self.classes):
            clsexc = self.find_exception('class', classname)
            if clsexc and clsexc.get('ignore') == 'true':
                continue

            cls = ET.SubElement(root, 'class', name=classname)
            self.copyExceptionData(cls, clsexc)
            self.pruneComment(cls)


            for method in sorted(self.classes[classname], key=lambda x: x['selector']):

                methodExc = self.find_exception(
                        'method', method['selector'],
                        exceptions=clsexc, nameattr='selector')

                # The 'ignore' atribute on methods is slight different than
                # on other elements: copy to the metadata file.
                #if methodExc and methodExc.get('ignore') == 'true':
                #    continue

                meth = ET.SubElement(cls, 'method', selector=method['selector'])

                if method['class_method']:
                    meth.set('class_method', 'true')
                if method['variadic']:
                    meth.set('variadic', 'true')
                if method.get('comment'):
                    meth.set('comment', method['comment'])

                self.copyExceptionData(meth, methodExc)
                self.pruneComment(meth)


                if len(method['returns']) == 2:
                    ret, ret64 = method['returns']
                    hints = {}
                else:
                    ret, ret64, hints = method['returns']
                exc = self.find_exception(
                        'retval', None, exceptions = methodExc)

                if self.isPointerType(ret) or (ret64 and self.isPointerType(ret64)) or (ret in self.special_type_encodings):
                    e = ET.SubElement(meth, 'retval', type=ret)
                    if ret64 and ret64 != ret:
                        e.set('type64', ret64)
                    for k, v in hints.items():
                        e.set(k, v)
                    self.copyExceptionData(e, exc, copyChildren=True)
                    self.pruneTypeInfo(e, 'type', ret, ret64)
                    self.pruneComment(e)

                elif exc is not None or hints:
                    e = ET.SubElement(meth, 'retval')
                    for k, v in hints.items():
                        e.set(k, v)
                    self.copyExceptionData(e, exc, copyChildren=True)
                    self.pruneTypeInfo(e, 'type', ret, ret64)
                    self.pruneComment(e)

                for argidx in sorted(method['arguments']):
                    if len(method['arguments'][argidx]) == 2:
                        arg, arg64 = method['arguments'][argidx]
                        meta = None
                    else:
                        arg, arg64, meta = method['arguments'][argidx]

                    exc = self.find_exception(
                            'method', method['selector'],
                            child_tag='arg', child_index=argidx,
                            exceptions=clsexc, nameattr='selector')

                    e = ET.SubElement(meth, 'arg', index=str(argidx), type=arg)
                    if arg64 and arg64 != arg:
                        e.set('type64', arg64)

                    if meta is not None:
                        for k, v in meta.items():
                            e.set(k, v)

                    self.copyExceptionData(e, exc, copyChildren=True)
                    self.pruneTypeInfo(e, 'type', arg, arg64)
                    self.pruneComment(e)

            if clsexc is not None:
                self.copyUnhandledNodes(clsexc, 'method', 
                        self.classes[classname],
                        nameAttr='selector', getName=lambda x:x['selector'])

        print >>fp, HEADER
        indentET(root)
        tree = ElementTree(root)
        tree.write(fp)
        fp.write('\n')

    def copyUnhandledNodes(self, root, cls, items, 
                                getName=lambda node: node[0],
                                nameAttr='name'):
        """
        Copy exception nodes that weren't used yet.
        
        Used to avoid loosing information when updating the metadata on
        an older OS-release.
        """

        for node in self.exceptionNodes(cls):
            nm = node.get(nameAttr)
            for cur in items:
                name = getName(cur)
                if nm == name:
                    break
            else:
                e = ET.SubElement(root, node.tag, name=nm)
                self.copyExceptionData(e, node)
                if node is not None:
                    for child in node:
                        e.append(child)

    def emitExceptionsXML(self, fp):

        root = ET.Element('signatures', version='1.0')

        for dep in self.dependencies:
            path = locate_framework(dep)
            exc = self.find_exception('depends_on',  path, nameattr='path')
            if exc is None:
                continue

            e = ET.SubElement(root, 'depends_on', path=path)
            self.copyExceptionData(e, exc)
            self.pruneComment(e)

        for name, encoded, encoded64 in sorted(self.structs):
            exc = self.find_exception('struct', name)

            if name.startswith('_') and exc is None:
                continue

            e = ET.SubElement(root, 'struct', name=name)
            if exc is not None and exc.get('ignore') == 'true':
                e.set('ignore', 'true')

            self.copyExceptionData(e, exc)

        self.copyUnhandledNodes(root, 'struct', self.structs)

        for name, encoded, encoded64 in sorted(self.opaque_pointers):
            exc = self.find_exception('opaque', name)
            if exc is None and name.startswith('_'):
                continue

            e = ET.SubElement(root, 'opaque', name=name)

            if exc is not None and exc.get('ignore') == 'true':
                e.set('ignore', 'true')

            self.copyExceptionData(e, exc)

        self.copyUnhandledNodes(root, 'opaque', self.opaque_pointers)

        for name, encoded, encoded64 in sorted(self.cftypes):
            e = ET.SubElement(root, 'cftype', name=name)

            exc = self.find_exception('cftype', name)
            if exc is not None and exc.get('ignore') == 'true':
                e.set('ignore', 'true')

            if name.endswith('Ref'):
                fn = name[:-3] + 'GetTypeID'
                if self.haveFunction(fn):
                    e.set('gettypeid_func', fn)

            self.copyExceptionData(e, exc)

        self.copyUnhandledNodes(root, 'cftype', self.cftypes)

        for name, encoded, encoded64 in sorted(self.globthings):
            exc = self.find_exception('constant', name)
            if exc is not None:
                e = ET.SubElement(root, 'constant', name=name)

                if exc.get('ignore') == 'true':
                    e.set('ignore', 'true')

                self.copyExceptionData(e, exc)

        self.copyUnhandledNodes(root, 'constant', self.globthings)

        for item in sorted(self.enums):
            name = item[0]
            exc = self.find_exception('enum', name)
            if exc is not None:
                e = ET.SubElement(root, 'enum', name=name)

                if exc.get('ignore') == 'true':
                    e.set('ignore', 'true')

                self.copyExceptionData(e, exc)

        self.copyUnhandledNodes(root, 'constant', self.enums)

        for name, tp, value, value64 in sorted(self.strvals):
            exc = self.find_exception('string_constant', name)
            if exc is not None:
                e = ET.SubElement(root, 'string_constant', name=name)

                if exc.get('ignore') == 'true':
                    e.set('ignore', 'true')

                self.copyExceptionData(e, exc)

        self.copyUnhandledNodes(root, 'string_constant', self.strvals)

        for name in sorted(self.null_const):
            exc = self.find_exception('null_const', name)
            if exc is not None:
                e =  ET.SubElement(root, 'null_const', name=name)
                self.copyExceptionData(e, exc)
                if exc.get('ignore') == 'true':
                    e.set('ignore', 'true')

        self.copyUnhandledNodes(root, 'null_const', self.null_const)

        for nm, (retType, retType64), arginfo, variadic in sorted(self.inline_functions):
            exc = self.find_exception('function', nm)
            if exc is not None and exc.get('ignore') == 'true':
                e = ET.SubElement(root, 'function', name=nm, ignore='true')
                continue

            func = None

            def makeFunction():
                if func is not None: return func

                f = ET.SubElement(root, 'function', name=nm, inline='true')
                if variadic:
                    f.set('variadic', 'true')

                return f

            if exc:
                func = makeFunction()
                self.copyExceptionData(func, exc)

            retexc = self.find_exception('function', nm, 'retval')

            isCreateOrCopy = False
            if self.isCFType(retType) and ('Create' in nm or 'Copy' in nm):
                isCreateOrCopy = True

            if retexc or self.isPointerType(retType) or isCreateOrCopy:
                func = makeFunction()
                e = ET.SubElement(func, 'retval', type=retType)
                if retType64:
                    e.set('type64', retType64)

                # Some heuristics, to be done before merging the exception
                # data
                if isCreateOrCopy:
                    if self.isCFType(retType):
                        e.set('already_cfretained', 'true')
                    else:
                        e.set('already_retained', 'true')

                    # This sucks, need to refactor...
                    del e.attrib['type']
                    if 'type64' in e.attrib:
                        del e.attrib['type64']

                self.copyExceptionData(e, retexc, copyChildren=True)
                self.pruneTypeInfo(e, 'type', retType, retType64)


            for idx, arg in enumerate(arginfo):
                exc = self.find_exception('function', nm, 'arg', idx)

                if exc or self.isPointerType(arg['encoded']):
                    func = makeFunction()
                    e = ET.SubElement(func, 'arg', index=str(idx))
                    e.set('type', arg['encoded'])
                    if arg.get('encoded64'):
                        e.set('type64', arg['encoded64'])

                    self.copyExceptionData(e, exc, copyChildren=True)
                    self.pruneTypeInfo(e, 'type', arg['encoded'], arg.get('encoded64'))

        for nm, (retType, retType64, retMeta), arginfo, variadic in sorted(self.functions):
            exc = self.find_exception('function', nm)
            if exc is not None and exc.get('ignore') == 'true':
                e = ET.SubElement(root, 'function', name=nm, ignore='true')
                continue

            func = None

            def makeFunction():
                if func is not None: return func

                f = ET.SubElement(root, 'function', name=nm)
                if variadic:
                    f.set('variadic', 'true')

                return f

            if variadic:
                func = makeFunction()

            if exc is not None:
                func = makeFunction()
                self.copyExceptionData(func, exc)

            retexc = self.find_exception('function', nm, 'retval')

            isCreateOrCopy = False
            if self.isCFType(retType) and ('Create' in nm or 'Copy' in nm):
                isCreateOrCopy = True

            if retexc is not None or self.isPointerType(retType) or isCreateOrCopy or retMeta:
                func = makeFunction()
                e = ET.SubElement(func, 'retval', type=retType)
                if retType64:
                    e.set('type64', retType64)

                for k, v in retMeta.items():
                    e.set(k, v)

                # Some heuristics, to be done before merging the exception
                # data
                if isCreateOrCopy:
                    if self.isCFType(retType):
                        e.set('already_cfretained', 'true')
                    else:
                        e.set('already_retained', 'true')

                    # This sucks, need to refactor...
                    del e.attrib['type']
                    if 'type64' in e.attrib:
                        del e.attrib['type64']

                self.copyExceptionData(e, retexc, copyChildren=True)
                self.pruneTypeInfo(e, 'type', retType, retType64)


            for idx, arg in enumerate(arginfo):
                exc = self.find_exception('function', nm, 'arg', idx)

                if exc is not None or self.isPointerType(arg['encoded']) or 'numeric' in arg:
                    func = makeFunction()
                    e = ET.SubElement(func, 'arg', index=str(idx))
                    e.set('type', arg['encoded'])
                    if arg.get('encoded64'):
                        e.set('type64', arg['encoded64'])

                    if 'numeric' in arg:
                        e.set('numeric', arg['numeric'])

                    self.copyExceptionData(e, exc, copyChildren=True)
                    self.pruneTypeInfo(e, 'type', arg['encoded'], arg.get('encoded64'))

        self.copyUnhandledNodes(root, 'function', self.functions + self.inline_functions)

        for nm, methods in sorted(self.informal_protocols):
            protoexc = self.find_exception('informal_protocol', nm)
            if protoexc is not None and protoexc.get('ignore') == 'true':
                e = ET.SubElement(root, 'informal_protocol', name=nm, ignore='true')
                continue

            if protoexc is None:
                continue

            # Need to check what exceptions there can be for informal 
            # protocols.
           
            proto = None
            def makeProto():
                if proto is not None:
                    return proto

                result =  ET.SubElement(root, 'informal_protocol', name, nm)
                self.copyExceptionData(result, protoexc)
                return result

            for m in sorted(methods, key=lambda item: item['selector']):
                exc = self.find_exception('method', nm, m['selector'], nameattr='selector', exceptions=protoexc)
                if exc is not None:
                    proto = makeProto()
                    e = ET.SubElement(proto, 'method')
                    self.copyExceptionData(e, exc)

        # FIXME: copy unhandled informal protocol metadata

        for classname in sorted(self.classes):
            clsexc = self.find_exception('class', classname)

            cls = None
            def getClass():
                if cls:
                    return cls
                r  = ET.SubElement(root, 'class', name=classname)
                self.copyExceptionData(r, clsexc)
                return r

            if clsexc and clsexc.get('ignore') == 'true':
                cls = getClass()
                continue

            for method in sorted(self.classes[classname], key=lambda x: x['selector']):

                methodExc = self.find_exception(
                        'method', method['selector'],
                        exceptions=clsexc, nameattr='selector')

                meth = None

                def getMethod():
                    if meth is not None:
                        return cls, meth
                    c = getClass()
                    m = ET.SubElement(c, 'method', selector=method['selector'])
                    self.copyExceptionData(m, methodExc)
                    return c, m


                if methodExc:
                    cls, meth = getMethod()
                    if methodExc.get('ignore') == 'true':
                        continue

                if len(method['returns']) == 2:
                    ret, ret64 = method['returns']
                    hints = {}
                else:
                    ret, ret64, hints = method['returns']

                exc = self.find_exception(
                        'retval', None, exceptions = methodExc)

                if self.isPointerType(ret) or (ret64 and self.isPointerType(ret64)) or ret == objc._C_SEL: 
                    # Ignore special_encodings for the exceptions file:
                    # or ret in self.special_type_encodings:
                    if meth is None:
                        cls, meth = getMethod()
                    e = ET.SubElement(meth, 'retval', type=ret)
                    if ret64 and ret64 != ret:
                        e.set('type64', ret64)
                    for k, v in hints.items():
                        e.set(k, v)
                    self.copyExceptionData(e, exc, copyChildren=True)
                    self.pruneTypeInfo(e, 'type', ret, ret64)

                elif exc is not None or hints:
                    cls, meth = getMethod()
                    e = ET.SubElement(meth, 'retval')
                    for k, v in hints.items():
                        e.set(k, v)
                    self.copyExceptionData(e, exc, copyChildren=True)
                    self.pruneTypeInfo(e, 'type', ret, ret64)

                for argidx in sorted(method['arguments']):
                    if len(method['arguments'][argidx]) == 2:
                        arg, arg64 = method['arguments'][argidx]
                        meta = None
                    else:
                        arg, arg64, meta = method['arguments'][argidx]


                    exc = self.find_exception(
                            'method', method['selector'],
                            child_tag='arg', child_index=argidx,
                            exceptions=clsexc, nameattr='selector')

                    if arg in self.special_type_encodings:
                        # Don't add these to the exceptions file unless there
                        # is other metadata.
                        if exc is not None:
                            cls, meth = getMethod()
                            e = ET.SubElement(meth, 
                                    'arg', index=str(argidx))

                            # First set heuristic  metadata
                            if meta is not None:
                                for k, v in meta.items():
                                    e.set(k, v)
                            
                            # Then the values from the metadat file
                            self.copyExceptionData(e, exc, copyChildren=True)
                            self.pruneTypeInfo(e, 'type', arg, arg64)
                        continue

                    cls, meth = getMethod()
                    e = ET.SubElement(meth, 'arg', index=str(argidx), type=arg)
                    if arg64 and arg64 != arg:
                        e.set('type64', arg64)
                    if meta is not None:
                        for k, v in meta.items():
                            e.set(k, v)

                    self.copyExceptionData(e, exc, copyChildren=True)
                    self.pruneTypeInfo(e, 'type', arg, arg64)

                if meth and method['class_method']:
                        meth.set('class_method', 'true')

                if method['variadic']:
                    cls, meth = getMethod()
                    meth.set('variadic', 'true')

                if method.get('comment'):
                    cls, meth = getMethod()
                    meth.set('comment', method['comment'])

            if clsexc is not None:
                self.copyUnhandledNodes(clsexc, 'method', 
                        self.classes[classname],
                        nameAttr='selector', getName=lambda x:x['selector'])

        self.copyUnhandledNodes(root, 'class', self.classes)

        print >>fp, HEADER
        indentET(root)
        tree = ElementTree(root)
        tree.write(fp)

    def stripStructFieldNames(self, encoding):
        return gStructFieldNameRe.sub('', encoding)

    def loadExceptionsXML(self, fname):
        self.exceptions = ET.parse(fname)

        # On Leopard Apple has started removing struct tags from some typedef-ed
        # definitions. This means the @encode-d value no longer uniquely 
        # identifies said type. 
        # The workaround for this is to add our own, made-up, encoding to the
        # exception file and use that instead of the real one. Because we use
        # a made-up encoding we must also create metadata nodes for all methods
        # that have such types as a return or argument value.
        for node in self.exceptions.getroot().findall('struct'):
            if node.get('type'):
                # Ensure that this type gets used throughout: add the type
                # to the encodings dict and to special_type_encodings
                self.special_type_encodings.add(node.get('type'))

                nm = node.get('name')
                tp = self.stripStructFieldNames(node.get('type'))
                tp64 = self.stripStructFieldNames(node.get('type64'))

                self.types[nm] = tp
                self.types[nm + '*'] = objc._C_PTR + tp
                self.types64[nm] = tp64
                self.types64[nm + '*'] = objc._C_PTR + tp64

    def pruneComment(self, node):
        if 'comment' in node.attrib:
            del node.attrib['comment']

        for nm in gBooleanAttributesDefaultingFalse:
            if node.attrib.get(nm) == 'false':
                del node.attrib[nm]

        for nm in gBooleanAttributesDefaultingTrue:
            if node.attrib.get(nm) == 'true':
                del node.attrib[nm]

        # XXX: needed for migration:
        if 'already_retained' in node.attrib and 'already_cfretained' in node.attrib:
            del node.attrib['already_retained']

    def pruneTypeInfo(self, node, typeAttr, type, type64):
        """
        Remove the type attributes from an exception node when they aren't
        actually necessary, this keeps the metadata clean.
        """

        attrs = node.attrib

        for nm in gBooleanAttributesDefaultingFalse:
            if attrs.get(nm) == 'false':
                del node.attrib[nm]

        if typeAttr not in node.attrib and typeAttr + '64' not in node.attrib:
            return

        if typeAttr in attrs and attrs[typeAttr] is None:
            del node.attrib[typeAttr]

        if typeAttr + '64' in attrs and attrs[typeAttr + '64'] is None:
            del node.attrib[typeAttr]

        if attrs.get(typeAttr) != type:
            return

        elif self.isCFType(type) or self.isOpaquePointerType(type):
            # No need to the type info when it's already handled.
            pass

        elif type in self.special_type_encodings:
            # Type info cannot be stripped
            return

        elif typeAttr + '64' in attrs:
            if attrs[typeAttr + '64'] != type64:
                return

        for attr in gTypeModifierAttributes:
            if attr in attrs:
                break
        else:
            # We don't have real metadata, keep the types to make the work
            # of the metadata-maintainer easier.

            if typeAttr not in node.attrib or not self.hasModifier(node.attrib[typeAttr]):
                return

        # There doesn't seem to be a public API for this??
        if typeAttr in attrs:
            del node.attrib[typeAttr]
        if typeAttr + '64' in attrs:
            del node.attrib[typeAttr + '64']


    def copyExceptionData(self, node, exceptionNode, copyChildren=False):
        """
        Copy exception data for this node
        """
        if exceptionNode is None:
            return

        for attrname in gExceptionAttributes.get(None, ()):
            value = exceptionNode.get(attrname)
            if value:
                node.set(attrname, value)

        for attrname in gExceptionAttributes.get(node.tag, ()):
            value = exceptionNode.get(attrname)
            if value:
                node.set(attrname, value)

            if value and (attrname == 'type' or attrname == 'type64'):
                if value.startswith('@encode'):
                    value, value64 = self.encodedType(value[7:-1])
                    if attrname.endswith('64'):
                        node.set(attrname, value64)
                    else:
                        node.set(attrname, value)

        if copyChildren:
            for child in exceptionNode.getchildren():
                attrib = dict(child.attrib)
                if 'comment' in attrib:
                    del attrib['comment']

                ET.SubElement(node, child.tag, **attrib)


    def _initTypes(self):

        # The type 'BOOL' is an alias for one of the small integer types,
        # expecitly covert to objc._C_BOOL to allow us to emit this into
        # the metadata
        for tp in ('BOOL', 'Boolean', 'CGPDFBoolean' ):
            self.types[tp] = objc._C_BOOL
            self.types64[tp] = objc._C_BOOL
            self.types[tp+'*'] = objc._C_PTR + objc._C_BOOL
            self.types64[tp+'*'] = objc._C_PTR + objc._C_BOOL
            self.types[tp+' *'] = objc._C_PTR + objc._C_BOOL
            self.types64[tp+' *'] = objc._C_PTR + objc._C_BOOL


        if hasattr(objc, '_C_CPPBOOL'):
            self.types['bool'] = objc._C_CPPBOOL
            self.types['bool*'] = objc._C_PTR + objc._C_CPPBOOL
            self.types['bool *'] = objc._C_PTR + objc._C_CPPBOOL

        for tp in ('unichar', 'UniChar'):
            for col in (self.types, self.types64):
                col[tp] = objc._C_UNICHAR
                col[tp+'*'] = objc._C_PTR + objc._C_UNICHAR
                col[tp+' *'] = objc._C_PTR + objc._C_UNICHAR
                col[tp+'**'] = objc._C_PTR + objc._C_PTR + objc._C_UNICHAR
                col[tp+' **'] = objc._C_PTR + objc._C_PTR + objc._C_UNICHAR
                col[tp+'* *'] = objc._C_PTR + objc._C_PTR + objc._C_UNICHAR
                col[tp+' * *'] = objc._C_PTR + objc._C_PTR + objc._C_UNICHAR

                col['const ' + tp] = objc._C_CONST + objc._C_UNICHAR
                col['const ' + tp + '*' ] = objc._C_CONST + objc._C_PTR + objc._C_UNICHAR
                col['const ' + tp + ' *' ] = objc._C_CONST + objc._C_PTR + objc._C_UNICHAR

        if hasattr(objc, '_C_CHAR_AS_INT'):
            for tp in ('int8_t',):
                for col in self.types, self.types64:
                    col[tp] = objc._C_CHAR_AS_INT
                    col[tp + '*'] = objc._C_PTR + objc._C_CHAR_AS_INT
                    col[tp + ' *'] = objc._C_PTR + objc._C_CHAR_AS_INT
                    col[tp + '**'] = objc._C_PTR + objc._C_PTR + objc._C_CHAR_AS_INT
                    col[tp + '* *'] = objc._C_PTR + objc._C_PTR + objc._C_CHAR_AS_INT
                    col[tp + ' **'] = objc._C_PTR + objc._C_PTR + objc._C_CHAR_AS_INT
                    col[tp + ' * *'] = objc._C_PTR + objc._C_PTR + objc._C_CHAR_AS_INT
                    col['const ' + tp] = objc._C_CONST + objc._C_CHAR_AS_INT
                    col['const ' + tp + '*'] = objc._C_CONST + objc._C_PTR + objc._C_CHAR_AS_INT
                    col['const ' + tp + ' *'] = objc._C_CONST + objc._C_PTR + objc._C_CHAR_AS_INT


        # CFTypeRef is a typedef for 'void*', which isn't exactly useful for
        # the metadata files.
        self.types['CFTypeRef'] = objc._C_ID
        self.types64['CFTypeRef'] = objc._C_ID
        self.types['CFTypeRef*'] = objc._C_PTR + objc._C_ID
        self.types64['CFTypeRef*'] = objc._C_PTR + objc._C_ID

    def scanClasses(self):
        """
        Load the framework and look for difficult methods in said framework.

        This is crude as hell, but helps in 
        """
        def haveMeta(cls, sel):
            for info in self.classes.get(cls.__name__, ()):
                if info['selector'] == sel:
                    return True

            return False

        framework_name = filter(None, os.path.split(self.framework))[0]
        framework_path = unicode(
                os.path.dirname(locate_framework(framework_name)), 
                sys.getfilesystemencoding())

        NSBundle = objc.lookUpClass('NSBundle')
        bundle = NSBundle.bundleWithPath_(framework_path)
        bundle.load()
        for cls in objc.getClassList():
            if NSBundle.bundleForClass_(cls) is not bundle:
                continue

            for methName in dir(cls):
                try:
                    meth = getattr(cls, methName)
                except AttributeError:
                    continue

                if not isinstance(meth, objc.selector):
                    continue

                selector = meth.selector

                if haveMeta(cls, selector):
                    continue

                meta = {}

                parts = objc.splitSignature(meth.native_signature)
                changed_parts = objc.splitSignature(meth.signature)
                if parts[0].startswith('^'):
                    # This is not quite true, but not really a problem either...
                    meta['returns'] = (parts[0], parts[0])
                    meta['arguments'] = {}
                    if changed_parts[0] != parts[0]:
                        if changed_parts[0].endswith(parts[0]):
                            meta['returns'] = meta['returns'] + ({
                                    'type_modifier': changed_parts[0][:len(changed_parts[0])-len(parts[0])],
                            },)
                        else:
                            meta['returns'] = changed_parts[0], changed_parts[0]

                for idx, (p, cp) in enumerate(zip(parts[1:], changed_parts[1:])):
                    if p.startswith('^'):
                        meta.setdefault('arguments', {})[idx] = (p, p, {})
                        if p != cp:
                            if cp.endswith(p):
                                meta['arguments'][idx] = (p, p, {
                                    'type_modifier': cp[:len(cp)-len(p)]
                                })
                            else:
                                meta['arguments'][idx] = (cp, cp, {})




                if meta:
                    meta['selector'] = selector
                    meta['class_method'] = meth.isClassMethod
                    meta['variadic'] = False
                    meta['comment'] = "framework scan"

                    self.classes.setdefault(cls.__name__, []).append(meta)

    def scan(self):
        """
        Scan framework headers.
        """

        framework_name = filter(None, os.path.split(self.framework))[0]
        framework_path = unicode(
                os.path.dirname(locate_framework(framework_name)), 
                sys.getfilesystemencoding())

    
        scanner = self.scanner = FrameworkScanner()

        if self.types is None:
            self.types = typedict()
        self._initTypes()

        self.ignores = set([
            CompilerDirective,
            BlockComment,
            SingleLineComment,
            CPPDecls,
            StaticInlineFunctionPrototype,
            SC_SCHEMA_DECLARATION,

            # Stuff below here might be interesting
            UninterestingStruct,
            MacroDefine,
            AttributeCrap,
        ])

        self.processTokens(scanner.scanframework(self.framework))


    def _processGlobalThing(self, token):
        name = token['name'].strip()

        if ',' in name:
            names = name.split(',')
            name = names[0]
            names = names[1:]
        else:
            names = []
            
        if name[0] == '_': 
            # Skip private variables
            return

        tp, tp64 = self.encodedType(token['type'])
        if not tp:
            return

        while tp and tp[0] == objc._C_CONST:
            tp = tp[1:]

        while tp64 and tp64[0] == objc._C_CONST:
            tp64 = tp64[1:]

        self.globthings.append((unicode(name), tp, tp64))


        # C's type rules are quaint(sp?), strip indirection
        # from the type we have now.
        type = token['type'].strip()
        while type.endswith('*'):
            type = type[:-1]

        for nm in names:
            nm = nm.strip()
            indirection = ''
            while nm[0] == '*':
                indirection += '*'
                nm = nm[1:].strip()

            tp, tp64 = self.encodedType(type + indirection)
            self.globthings.append((unicode(nm), tp, tp64))




    def _processNamedEnum(self, token):
        nm = token.matches()[-1]['name'].strip()

        self._processEnum(token)


    def _processEnum(self, enum):
        i = -1
        for token in enum.matches():
            if isinstance(token, (EnumValueMember, EnumBareMember)):
                    name = token['name']

                    # Need to compile 3-way (intel-32, ppc-32, and something-64-bit) to
                    # get all possible variations of values. Compiling multiple times is
                    # expensive, therefore avoid cross-architecture compiling when the
                    # value is obviously a plain integer or has an implicit value.
                    if isinstance(token, EnumValueMember) and not simpleValue(token['value']):
                        data, data64, dataPPC = self._compileAndRun(
                            ENUM_MAIN % locals(), ENUM_DEFINITIONS, 

                            # I have no idea why, but at least some system
                            # headers contain '#if 0'-ed out code...
                            allowFailure=True, 
                            ppcAndIntel=True)
                    else:
                        data, data64 = self._compileAndRun(
                            ENUM_MAIN % locals(), ENUM_DEFINITIONS, 

                            # I have no idea why, but at least some system
                            # headers contain '#if 0'-ed out code...
                            allowFailure=True, 
                            ppcAndIntel=False)
                        dataPPC = ''

                    if data:
                        tp, value = data.split('\n', 1)
                        value = value
                    else:
                        tp = None
                        value = ''

                    if data64 and '\n' in data64:
                        tp64, value64 = data64.split('\n', 1)
                        value64 = value64
                    else:
                        tp64 = None
                        value64 = ''

                    if tp is None:
                        tp = tp64

                    if tp is None:
                        continue

                    if dataPPC:
                        try:
                            tpPPC, valuePPC = dataPPC.split('\n', 1)
                        except ValueError:
                            print "tpPPC", `dataPPC`
                            tpPPC = valuePPC = None

                        if tp is None:
                            tp = tpPPC
                    else:
                        tpPPC = None
                        valuePPC = None
                
                    if name.endswith('FunctionKey'):
                        # Special casing for for keycode definitions in AppKit
                        if value is not None:
                            value = unichr(int(value))
                        if value64 is not None:
                            value64 = unichr(int(value64))

                        self.strvals.append((name, objc._C_ID, value, value64))
                    elif tp in [ objc._C_ID, objc._C_CHARPTR ]:
                        self.strvals.append((name, tp, value, value64))
                    else:
                        self.enums.append((name, value, value64, valuePPC))
                        
            elif isinstance(token, (EnumEnd, NamedEnumEnd)):
                pass

            elif type(token) in self.ignores:
                pass

            else:
                raise RuntimeError, "Unhandled token in Enum definition: %r"%(
                        token,)

    def _processFunctionCallDefine(self, token):
        if token['name'].startswith('_'): return

        name = token['name']
        body = token['body'].strip()
        if not body.startswith('CFSTR("'):
            return

        name = token['name'].strip()
        data, data64, dataPPC = self._compileAndRun(
                ENUM_MAIN % locals(), ENUM_DEFINITIONS, allowFailure=1, ppcAndIntel=1)

        if data:
            if '\n' in data:
                tp, value = data.split('\n', 1)

            else:
                logging.warning("ignore hard define %s", name)
                return

        else:
            tp = None
            value=None

        if data64:
            tp64, value64 = data64.split('\n', 1)
            value64 = value64

            if tp is None:
                tp = tp64
        else:
            tp64 = None
            value64 = None

        if dataPPC:
            tpPPC, valuePPC = dataPPC.split('\n', 1)

            if tp is None:
                tp = tpPPC
        else:
            tpPPC = None
            valuePPC = None


        if tp in [ objc._C_FLT, objc._C_DBL]:

            # Printf's '%g' might print a value that's not clearly recognizable
            # as float. Compensate for that.
            for c in value:
                if not c.isdigit():
                    break
            else:
                value = value + '.0'

            for c in value64:
                if not c.isdigit():
                    break
            else:
                value64 = value64 + '.0'

            for c in valuePPC:
                if not c.isdigit():
                    break
            else:
                valuePPC = valuePPC + '.0'

        if not value and not value64 and not valuePPC:
            logging.info("No value for enum label %s", name)
            return
            return

        if tp in [ objc._C_ID, objc._C_CHARPTR, self.cfstringtype, objc._C_CONST + self.cfstringtype ]:
            if tp == objc._C_CHARPTR:
                self.strvals.append((name, objc._C_CHARPTR, value, value64))
            else:
                self.strvals.append((name, objc._C_ID, value, value64))

        else:
            self.enums.append((name, value, value64, valuePPC))

    

        
    def _processSimpleDefine(self, token):
        # XXX: Hardcoded list, move to exceptions file?
        logging.debug("Simple define %s", token['name'])
        if token['name'].startswith('_'): return
        if token['name'] in ('NULL',): return
        if token['value'].strip().startswith('AVAILABLE_MAC_OS_X_VERSION_'): return
        if token['value'].strip().startswith('DEPRECATED_'): return
        if token['value'] in ('SHRT_MAX',): return

        if token['value'] == 'nil':
            self.null_const.append(token['name'])
            return

        # Note: we pick up some definitions in #ifdef-out parts of the code
        # (such as 64-bit only definition on a 32-bit build, therefore allow
        # compilation to fail).
        name = token['name'].strip()
        if name == 'kCGNotifyEventTapRemoved':
            allowFailure=False
        else:
            allowFailure=True
        data, data64, dataPPC = self._compileAndRun(
                ENUM_MAIN % locals(), ENUM_DEFINITIONS, allowFailure=allowFailure, ppcAndIntel=1)

        if data:
            if '\n' in data:
                tp, value = data.split('\n', 1)

            else:
                tp = data
                if tp == objc._C_PTR + objc._C_VOID and token['value'] == 'NULL':
                    self.null_const.append(name)

                else:
                    logging.warning("ignore hard define %s", name)

                return

        else:
            tp = None
            value=None

        if data64:
            tp64, value64 = data64.split('\n', 1)
            value64 = value64

            if tp is None:
                tp = tp64
        else:
            tp64 = None
            value64 = None

        if dataPPC:
            if '\n' in dataPPC:
                tpPPC, valuePPC = dataPPC.split('\n', 1)
            else:
                logging.info("Corrupt output on PPC for %s for %s", name, dataPPC)
                tpPPC, valuePPC = dataPPC, ''
            if tp is None:
                tp = tpPPC
        else:
            tpPPC = None
            valuePPC = None

        if not value and not value64 and not valuePPC:
            logging.warning("No value for #define %s %r %r %r", name, value, value64, valuePPC)
            logging.warning("data: %r %r %r", data, data64, dataPPC)
            return

        if tp in [ objc._C_FLT, objc._C_DBL]:

            # Printf's '%g' might print a value that's not clearly recognizable
            # as float. Compensate for that.
            for c in value:
                if not c.isdigit():
                    break
            else:
                value = value + '.0'

            if value64:
                for c in value64:
                    if not c.isdigit():
                        break
                else:
                    value64 = value64 + '.0'

            if valuePPC:
                for c in valuePPC:
                    if not c.isdigit():
                        break
                else:
                    valuePPC = valuePPC + '.0'

        if not value and not value64 and not valuePPC:
            logging.info("No value for #define %s", name)
            return
    
        if tp in [ objc._C_ID, objc._C_CHARPTR, self.cfstringtype, objc._C_CONST + self.cfstringtype ]:
            if tp == objc._C_CHARPTR:
                self.strvals.append((name, objc._C_CHARPTR, value, value64))
            else:
                self.strvals.append((name, objc._C_ID, value, value64))

        else:
            self.enums.append((name, value, value64, valuePPC))

    def _processDependencies(self, token):
        dep = os.path.dirname(token.infoTuple()[0])
        if dep and dep not in self.dependencies:
            self.dependencies.add(dep)
            self.extractTypes(dep)
            self.imports.append(dep)
            self.tryLoadMetaData(dep)

    def tryLoadMetaData(self, framework):
        """
        Try to load metadata for a framework we're depending on, this is
        needed to correctly recognize opaque pointers and CF-types that are
        defined in that framework.
        """
        if framework in self.loadedMetaData:
            return

        self.loadedMetaData.add(framework)
        logging.info('Try loading metadata for %s', framework)


        # NOTE: This will require some changes once the system's metadata is
        # useful.

        # Need to find bug that causes this to fail...
        #return

        try:
            xml = pkg_resources.resource_string(framework, 'PyObjC.bridgesupport') 
        except:
            
            # XXX: to be removed
            pth = os.path.join(
                        '..', 
                        'pyobjc-framework-%s'%(framework,),
                        'Lib',
                        framework,
                        'PyObjC.bridgesupport')
            if not os.path.exists(pth):
                if framework == 'CoreServices' and self.framework != 'CoreFoundation':
                    # CoreServices is an umbrella framework and the interesting
                    # info is in CoreFoundation
                    self.tryLoadMetaData('CoreFoundation')
                    for fn in os.listdir('/System/Library/Frameworks/CoreServices.framework/Frameworks'):
                        self.tryLoadMetaData(fn.split('.')[0])
                    return

                elif framework == 'Cocoa':
                    # Cocoa is a convenience framework that just links to these
                    # two:
                    self.tryLoadMetaData('Foundation')
                    self.tryLoadMetaData('AppKit')
                    return

                elif framework == 'ApplicationServices':
                    self.tryLoadMetaData('CoreFoundation')
                    for fn in os.listdir('/System/Library/Frameworks/ApplicationServices.framework/Frameworks'):
                        self.tryLoadMetaData(fn.split('.')[0])
                        return

                elif framework == 'Quartz':
                    self.tryLoadMetaData('CoreFoundation')
                    for fn in os.listdir('/System/Library/Frameworks/Quartz.framework/Frameworks'):
                        self.tryLoadMetaData(fn.split('.')[0])
                        return


                else:
                    for meta in ('Cocoa', 'Quartz'):
                        pth = os.path.join(
                            '..', 
                            'pyobjc-framework-%s'%(meta,),
                            'Lib',
                            framework,
                            'PyObjC.bridgesupport')
                        if os.path.exists(pth):
                            break

                    else:
                        logging.info('Failed loading metadata for %s', framework)
                        return

            xml = open(pth, 'rb').read()

        logging.debug("Loading metadata for %s", framework)

        meta = ET.parse(StringIO.StringIO(xml)).getroot()
        for node in meta.findall('cftype'):
            self._dep_cftypes.append(
                    (node.get('name'), node.get('type'), node.get('type64')))

        for node in meta.findall('opaque'):
            self._dep_opaque.append(
                    (node.get('name'), node.get('type'), node.get('type64')))

        # Deal with structs that have a custom encoding:
        for node in meta.findall('struct'):
            nm = node.get('name')
            tp = self.stripStructFieldNames(node.get('type', ''))
            tp64 = self.stripStructFieldNames(node.get('type64', ''))

            try:
                realTp, _ = self.encodedType(nm, framework)
            except:
                logging.debug("Cannot get encoded struct %s", nm) 
                continue

            if realTp != tp:
                self.special_type_encodings.add(tp)
                self.types[nm] = tp
                self.types[nm + '*'] = objc._C_PTR + tp
                self.types64[nm] = tp64
                self.types64[nm + '*'] = objc._C_PTR + tp64

        for node in meta.findall('depends_on'):
            path = node.get('path')
            if path is None: continue
            if not os.path.exists(path): continue

            # Some day this code will use the framework paths instead of 
            # plain framework names...
            frameworkName = os.path.split(path)[1]
            frameworkName = os.path.splitext(frameworkName)[0]
            self.tryLoadMetaData(frameworkName)

        logging.info('Done loading metadata for %s', framework)

    def _processStruct(self, token):
        tag = token['structname']
        self.plain_structs[tag] = token

        if tag in self.opaque_structs:
            # Whoops, it is a struct definition after all.
            name = self.opaque_structs[tag]
            if name.endswith('*'):
                name = name[:-1]
            self._processNamedStruct(token, name)
            opaques = []

            name = self.opaque_structs[tag]
            for entry in self.opaque_pointers:
                if entry[0] == name:
                    continue
                opaques.append(entry)
            self.opaque_pointers = opaques

            cfs = []
            for entry in self.cftypes:
                if entry[0] == name:
                    continue
                cfs.append(entry)
            self.cftypes = cfs


    def _processNamedStruct(self, token, name=None):
        # Skip structs containing function pointers, those cannot be
        # wrapped automaticly (yet?)
        if isinstance(token, Struct):
            externalname = name

        elif isinstance(token, NamedStruct):
            if contains_instances_of(token.matches(), FunctionStructMember): return

            externalname = token.matches()[-1]['name']
        else:
            externalname = token['name']

        ivarType = externalname
        data, data64 = self._compileAndRun(
                    IVAR_TYPE_MAIN, IVAR_TYPE_DEFINITIONS % locals(), 
                    allowFailure=True)

        # Found a struct without a struct tag. That's rather useless because
        # two different types may have the same structure (excluding field *names*)
        # Therefore make up a struct tag.
        if data and data[1] == '?':
            data = data[0] + '_' + externalname + data[2:]

        if data64 and data64[1] == '?':
            data64 = data64[0] + '_' + externalname + data64[2:]

        self.structs.append((externalname, data, data64))
        logging.debug("Struct definition for %s", externalname)


    def _processOpaqueNamedStruct(self, token):
        name = token['name']
        tag = token['label']
        const = token['const']
        indirect = normalize_type(token['indirection'])

        if not indirect and tag in self.plain_structs:
            self._processNamedStruct(token)
            return

        logging.debug("Process opaque struct: %(name)s"% token)

        if indirect:
            ivarType = name
            name = name 
        else:
            ivarType = name + '*'
            name = name + '*'

        data, data64 = self._compileAndRun(
                IVAR_TYPE_MAIN, IVAR_TYPE_DEFINITIONS % locals() )

        self.types[ivarType] = data

        exc = self.find_exception('cftype', ivarType)
        if exc is not None:
            self.cftypes.append((name, data, data64))
            return

        exc = self.find_exception('opaqaue', ivarType)
        if exc is not None:
            self.opaque_pointers.append((name, data, data64))
            return

        # Do a guestimate...
        if name.endswith('Ref') and tag.startswith('__'):
            # This is probably a CF-basd type
            self.cftypes.append((name, data, data64))

        else:
            self.opaque_pointers.append((name, data, data64))

        self.opaque_structs[tag] = name


    def _processUninterestingTypedef(self, token):
        # XXX: drop?
        if ',' in token['body']: return
        ptr = 0
        body = token['body'].split()

        alias = normalize_type(' '.join(body[:-1]))
        target = body[-1]

        if target in self.types:
            return


        if alias == 'CFTypeRef':
            self.cftypes.append((target, '@', '@'))

            # Also override the encoded type, for easier wrapping
            self.types[target] = '@'
            self.types64[target] = '@'
            return
       
        while alias[-1] == '*':
            alias = alias[:-1]
            ptr += 1
        while target[0] == '*':
            target = target[1:]
            ptr += 1
        if alias in self.types:
            if ptr == 1:
                prefix = objc._C_PTR
            elif ptr > 1:
                prefix = objc._C_PTR*ptr
            else:
                prefix = ''
            self.types[target] = prefix + self.types[alias]

        else:
            logging.info("Ignore simple typedef: %s -> %s", target, alias)


    def _processProtocol(self, token):
        # XXX: maybe collect these as informal protocols
        logging.info("Protocol: %s", token['name'])

        # Process nested definitions
        self.processTokens([ 
            t for t in token.matches()
                if type(t) not in (ProtocolEnd, SimpleMethodDef, SegmentedMethodDef)])

        clsexc = self.find_exception('class', 'NSObject')

        # Look for exceptions
        # XXX: refactor this loop, its the same as in _processInterface
        methods = []
        for t in token.matches():
            if isinstance(t, SimpleMethodDef):
                if t['kind'] == '-':
                    class_method=False
                else:
                    class_method=True

                returnType, returnType64 = self.encodedType(t['returntype'] or 'id')
                if self.isPointerType(returnType) or returnType in self.special_type_encodings:
                    methods.append(
                        dict(
                            selector=t['name'],
                            class_method=class_method,
                            returns=(returnType, returnType64),
                            arguments={},
                            variadic=False,
                        )
                )

            elif isinstance(t, SegmentedMethodDef):
                if t['kind'] == '-':
                    class_method=False
                else:
                    class_method=True

                isVariadic = False
               
                returnType, returnType64 = self.encodedType(t['returntype'] or 'id')
                haveSpecial = self.isPointerType(returnType) or returnType in self.special_type_encodings
                arguments = {}
                segs = []

                for idx, s in enumerate(t.matches()):
                    if isinstance(s, FunctionElipsisParameter):
                        isVariadic = True

                    if isinstance(s, MethodSegment):
                        segs.append(s['name'])
                        argType, argType64 = self.encodedType(s['type'] or 'id')
                        if argType is None:
                            # Compiler problems...
                            pass
                        elif self.isPointerType(argType) and normalize_type(s['type']) == 'NSError**':
                            # An NSError** argument, probably an output 
                            # argument
                            arguments[idx] = (argType, argType64, {
                                'type_modifier': 'o',
                                'null_accepted': 'true',
                            })
                            haveSpecial = True

                        elif self.isPointerType(argType) and normalize_type(s['type']) == 'CFErrorRef*':
                            # An NSError** argument, probably an output 
                            # argument
                            arguments[idx] = (argType, argType64, {
                                'type_modifier': 'o',
                                'null_accepted': 'true',
                            })
                            haveSpecial = True
                        elif (self.isPointerType(argType) and not self.hasModifier(argType)) or argType in self.special_type_encodings:
                            haveSpecial = True
                            arguments[idx] = (argType, argType64, None)


                selector = ''.join(segs)


                for i in range(len(segs)+1):
                    exc = self.find_exception(
                        'method', selector,
                        child_tag='arg', child_index=i,
                        exceptions=clsexc, nameattr='selector')
                    if exc is not None:
                        haveSpecial = True
                        if i not in arguments:
                            arguments[i] = (None, None, None)
                            haveSpecial = True


                if haveSpecial or isVariadic:
                    methods.append(
                        dict(
                            selector=''.join(segs),
                            class_method=class_method,
                            returns=(returnType, returnType64),
                            arguments=arguments,
                            variadic=isVariadic,
                        )
                    )


        if methods:
            # Always add to 'NSObject', that way all classes implementing this
            # protocol will see these exceptions.
            if 'NSObject' in self.classes:
                self.classes['NSObject'].extend(methods)
            else:
                self.classes['NSObject'] = methods




    def _processInterface(self, token):
        # Process nested definitions
        self.processTokens([ 
            t for t in token.matches()
                if type(t) not in (InterfaceEnd, SimpleMethodDef, SegmentedMethodDef, PropertyDefinition)])

        self.types[token['name'] + '*'] = objc._C_ID
        self.types64[token['name'] + '*'] = objc._C_ID
        self.types[token['name'] + '**'] = objc._C_PTR + objc._C_ID
        self.types64[token['name'] + '**'] = objc._C_PTR + objc._C_ID

        if token['name'] == 'NSObject' and token['category']:
            self._processInformalProtocol(token)
        
        logging.info("Class: %s (%s)", token['name'], token['category'])
        clsexc = self.find_exception('class', token['name'])

        methods = []
        for t in token.matches():
            if isinstance(t, PropertyDefinition):
                # @property
                typestr = t['type']
                options = t['options']
                if options is None:
                    options = []
                else:
                    options = [ v.strip() for v in options.split(',') ]

                haveSetter = True
                if 'readonly' in options:
                    haveSetter = False

                

                first = True


                for nm in t.matches():
                    if isinstance(nm, PropertyName):
                        nm = nm['name']
                    else:
                        continue

                    getterName = nm
                    setterName = 'set%s:'%(nm[0].upper() + nm[1:],)
                    for opt in options:
                        if opt.startswith('getter='):
                            getterName = opt.split('=', 1)[-1]
                        elif opt.startswith('setter='):
                            setterName = opt.split('=', 1)[-1]

                    curtype = typestr
                    while nm.startswith('*'):
                        curtype += '*'
                        nm = nm[1:].strip()


                    tp, tp64 = self.encodedType(curtype)

                    # First try to tell about the propertygetter
                    returnsMeta = {}
                    if self.isStringyType(tp, curtype):
                        returnsMeta['numeric'] = 'false'
                    
                    if self.isPointerType(tp) or tp in self.special_type_encodings or returnsMeta:
                        methods.append(
                            dict(
                                selector=getterName,
                                class_method=False,
                                returns=(tp, tp64, returnsMeta),
                                arguments={},
                                variadic=False,
                            )
                    )

                    # Then try to tell about the setter
                    if haveSetter:
                        if self.isPointerType(tp) or tp in self.special_type_encodings or returnsMeta:
                            methods.append(
                                dict(
                                    selector=setterName,
                                    class_method=False,
                                    returns=(objc._C_VOID, objc._C_VOID, {}),
                                    arguments={
                                        0: (tp, tp64, returnsMeta),
                                    },
                                    variadic=False,
                                )
                        )


                    if first:
                        # Strip indirection, pointer indirection only applies to the first name in 
                        # a list.
                        while typestr.endswith('*'):
                            typestr = typestr[:-1]
                        first = False




            elif isinstance(t, SimpleMethodDef):
                if t['kind'] == '-':
                    class_method=False
                else:
                    class_method=True

                returnType, returnType64 = self.encodedType(t['returntype'] or 'id')
                returnsMeta = {}
                if self.isStringyType(returnType, t['returntype'] or 'id'):
                    returnsMeta['numeric'] = 'false'

                if self.isPointerType(returnType) or returnType in self.special_type_encodings or returnsMeta:
                    methods.append(
                        dict(
                            selector=t['name'],
                            class_method=class_method,
                            returns=(returnType, returnType64, returnsMeta),
                            arguments={},
                            variadic=False,
                        )
                )

            elif isinstance(t, SegmentedMethodDef):
                if t['kind'] == '-':
                    class_method=False
                else:
                    class_method=True

                isVariadic = False
               
                returnType, returnType64 = self.encodedType(t['returntype'] or 'id')
                returnsMeta = {}
                if self.isStringyType(returnType, t['returntype'] or 'id'):
                    returnsMeta['numeric'] = 'false'

                haveSpecial = self.isPointerType(returnType) or returnType in self.special_type_encodings
                arguments = {}
                segs = []

                for idx, s in enumerate(t.matches()):
                    if isinstance(s, FunctionElipsisParameter):
                        isVariadic = True

                    if isinstance(s, MethodSegment):
                        segs.append(s['name'])
                        argType, argType64 = self.encodedType(s['type'] or 'id')
                        if argType is None:
                            # Compiler problems...
                            pass

                        elif self.isPointerType(argType) and normalize_type(s['type']) == 'NSError**':
                            # An NSError** argument, probably an output 
                            # argument
                            arguments[idx] = (argType, argType64, {
                                'type_modifier': 'o',
                                'null_accepted': 'true',
                            })
                            haveSpecial = True

                        elif (self.isPointerType(argType) and not self.hasModifier(argType)) or argType in self.special_type_encodings or argType == objc._C_SEL:
                            haveSpecial = True
                            arguments[idx] = (argType, argType64, None)

                        elif self.isStringyType(argType, s['type']):
                            haveSpecial = True
                            arguments[idx] = (argType, argType64, {
                                'numeric': 'false'})


                selector = ''.join(segs)


                for i in range(len(segs)+1):
                    exc = self.find_exception(
                        'method', selector,
                        child_tag='arg', child_index=i,
                        exceptions=clsexc, nameattr='selector')
                    if exc is not None:
                        haveSpecial = True
                        if i not in arguments:
                            arguments[i] = (None, None, None)
                            haveSpecial = True


                if haveSpecial or isVariadic:
                    methods.append(
                        dict(
                            selector=''.join(segs),
                            class_method=class_method,
                            returns=(returnType, returnType64, returnsMeta),
                            arguments=arguments,
                            variadic=isVariadic,
                        )
                    )


        if methods:
            if token['name'] in self.classes:
                self.classes[token['name']].extend(methods)
            else:
                self.classes[token['name']] = methods

    def _processInformalProtocol(self, token):
        logging.info("Informal protocol: %s", token['category'])
        methods = []
        for t in token.matches():
            # Add dict(selector=, encoding=, ...) to methods
            if isinstance(t, SimpleMethodDef):
                if t['kind'] == '-':
                    class_method = False
                else:
                    class_method = True

                returnType, returnType64 = self.encodedType(t['returntype'] or 'id')
                methods.append(
                    dict(
                        selector=t['name'],
                        class_method=class_method,
                        type=returnType + '@:',
                    )
                )

                if returnType64 and returnType64 != returnType:
                    methods[-1]['type64'] = returnType64 + '@:',

            elif isinstance(t, SegmentedMethodDef):
                if t['kind'] == '-':
                    class_method=False
                else:
                    class_method=True
        
                returnType, returnType64 = self.encodedType(t['returntype'] or 'id')
                types = [
                    returnType,
                    objc._C_ID,
                    objc._C_SEL,
                ]
                have64 = bool(returnType64)
                types64 = [
                    returnType64,
                    objc._C_ID,
                    objc._C_SEL,
                ]
                segs = []

                for s in t.matches():
                    if isinstance(s, MethodSegment):
                        segs.append(s['name'])
                        argType, argType64 = self.encodedType(s['type'] or 'id')
                        types.append(argType)
                        if not argType64:
                            have64 = False
                        types64.append(argType64)

                if None in types:
                    logging.warning("Cannot calcultate types for %s %s",
                            token['category'],
                            ''.join(segs))

                    continue


                methods.append(
                    dict(
                        selector=''.join(segs),
                        class_method=class_method,
                        type=''.join(types),
                    )
                )
                if have64 and types64 != types:
                    methods[-1]['type64'] = ''.join(types64)


        self.informal_protocols.append((token['category'], methods))

    def _processForwardClassReference(self, token):
        pass

    def _processForwardProtocolReference(self, token):
        pass

    def _processExportFunction(self, token):
        logging.debug('ExportFunction %s', token['name'])
        returns = token['returns']
        name = token['name'].strip()

        for item in self.functions:
            if item[0] == name:
                return

        if name.startswith('_'): 
            # Ignore  private functions, but have a hook to surpress
            # this behaviour
            exc = self.find_exception('function', name)
            if exc is None or exc.get('ignore', 'false') == 'true':
                return

        if isinstance(token, ExportVoidFunction):
            returnType, returnType64 = self.encodedType(returns)
            if returnType is None:
                logging.warning("Ignore function %s: cannot encode type %r",
                        name, returns)
                return
            returnMeta = {}
            if self.isStringyType(returnType, returns):
                returnMeta['numeric'] = 'false'

            self.functions.append((name, (returnType,returnType64, returnMeta), [], False))
            return


        variadic = False

        returnType, returnType64 = self.encodedType(returns)
        if returnType is None:
            logging.warning("Ignore function %s: cannot encode type %r",
                    name, returns)
            return

        returnMeta = {}
        if self.isStringyType(returnType, returns):
            returnMeta['numeric'] = 'false'

        arginfo = []
        for idx, arg in enumerate([ x for x in token.matches()[:-1] if not isinstance(x, BlockComment)]):
            if isinstance(arg, FunctionElipsisParameter):
                variadic = True
                continue

            nm = arg['name']
            tp = arg['type'].strip()

            if nm is None:
                # Sigh, getting the function parameter scanner correct is
                # a hopeless task, work around the problems here.
                m = re.match('(.*?)([A-Za-z_][A-Za-z0-9_]*)$', tp)
                if m:
                    tp = m.group(1).strip()
                    nm = m.group(2).strip()
                    if not tp:
                        tp = nm
                        nm = ''


            array = arg['array']
            if array:
                tp = tp + array
            arg = {}
            arginfo.append(arg)

            if nm is None:
                arg['type'] = tp
            else:
                arg['type'] = tp
                arg['name'] = nm.strip()


            try:
                arg['encoded'], arg['encoded64'] = self.encodedType(tp)
            except:
                print token
                raise

            if arg['encoded'] is None:
                logging.warning(
                    "Ignore function %s: cannot encode type %r for argument %d",
                    name, tp, idx)
                return

            if self.isStringyType(arg['encoded'], tp):
                arg['numeric'] = 'false'

        if isinstance(token, StaticInlineFunction):
            self.inline_functions.append((name, (returnType, returnType64), arginfo, variadic))
        else:
            self.functions.append((name, (returnType, returnType64, returnMeta), arginfo, variadic))


    def processTokens(self, tokenList):
        for token in ifilter(None, tokenList):
            if isinstance(token, CPPCrap):
                # Try to extract as much info as possible from CPPCrap
                if '#else' in token['body']:
                    text = token['body']
                    text = text[text.index('#else'):]
                    scn = Scanner(LEXICON)
                    self.processTokens(scn.iterscan(text))

            elif isinstance(token, GlobalThing):
                self._processGlobalThing(token)

            elif isinstance(token, Enum):
                self._processEnum(token)
            
            elif isinstance(token, NamedEnum):
                self._processNamedEnum(token)

            elif isinstance(token, Dependency):
                self._processDependencies(token)

            elif isinstance(token, SimpleDefine):
                self._processSimpleDefine(token)

            elif isinstance(token, FunctionCallDefine):
                self._processFunctionCallDefine(token)

            elif isinstance(token, NamedStruct):
                self._processNamedStruct(token)

            elif isinstance(token, Struct):
                self._processStruct(token)

            elif isinstance(token, OpaqueNamedStruct):
                self._processOpaqueNamedStruct(token)

            elif isinstance(token, UninterestingTypedef):
                self._processUninterestingTypedef(token)

            elif isinstance(token, Interface):
                self._processInterface(token)

            elif isinstance(token, Protocol):
                self._processProtocol(token)

            elif isinstance(token, ForwardClassReference):
                self._processForwardClassReference(token)

            elif isinstance(token, ForwardProtocolReference):
                self._processForwardProtocolReference(token)

            elif isinstance(token, (ExportFunction, ExportVoidFunction, StaticInlineFunction)):
                self._processExportFunction(token)

            else:
                if type(token) not in self.ignores:
                    print token
                    import pdb
                    pdb.Pdb().set_trace()



    def _compileAndRun(self, mainBody, globalDefinitions='', allowFailure=True, ppcAndIntel=False, additionalFramework=None):
        """Returns output, inserts 'mainBody' as the main body"""

        # Insert all include files we've seen so far instead of just
        # <framework/framework.h> because some frameworks don't have that
        # file.
        if not hasattr(self, 'scanner'):
            include_files = []
        else:
            include_files = self.scanner.seen

        # FSEvents is a separate framework, but also part of CarbonCore
        # which is the real version?
        include_files = [fn for fn in include_files if fn != 'FSEvents/FSEvents.h' ]

        if self.framework == self.link_framework:
            includes = '\n'.join(["#import <%s>"%(fn,) for fn in include_files])
        else:
            includes = '#import <%s/%s.h>'%(self.link_framework, self.link_framework)

        if additionalFramework:
            if os.path.exists('/System/Library/Frameworks/CoreServices.framework/Frameworks/%s.framework'%(additionalFramework)):
                pass
            elif os.path.exists('/System/Library/Frameworks/ApplicationServices.framework/Frameworks/%s.framework'%(additionalFramework)):
                pass
            else:
                includes += '\n#import <%s/%s.h>'%(additionalFramework, additionalFramework)

        contents = COMPILE_FILE % locals()

        fp = open('_scanframework.m', 'w')
        fp.write(contents)
        fp.close()

        try:
            # XXX: add redirection of stderr to /dev/null
            xit = subprocess.call('cc -pipe -o _scanframework _scanframework.m %s 2>/dev/null'%(
                self.CFLAGS), shell=True)

            if xit != 0:
                # Some frameworks, such as SyncServices contain some code that will only
                # work when the deployment target is at least 10.5...
                os.environ['MACOSX_DEPLOYMENT_TARGET']='10.5'
                xit = subprocess.call('cc -pipe -o _scanframework _scanframework.m %s 2>/dev/null'%(
                    self.CFLAGS), shell=True)
                del os.environ['MACOSX_DEPLOYMENT_TARGET']

            if xit != 0:
                logging.debug(
                    "Compilation failed for file contents\n---\n%s\n---",
                    contents)
                if allowFailure:
                    data = ''

                else:
                    raise RuntimeError("Compilation failed")

            else:

                data = subprocess.Popen(['./_scanframework'], stdout=subprocess.PIPE
                    ).communicate()[0]
                data = data.strip()

            if int(os.uname()[2].split('.')[0]) <= 8:
                data64 = ''

            else:
                os.environ['MACOSX_DEPLOYMENT_TARGET']='10.5'
                xit = subprocess.call(
                    'cc -pipe -o _scanframework _scanframework.m %s %s 2>/dev/null'%(
                    self.CFLAGS, self.CFLAGS64), shell=True)
                del os.environ['MACOSX_DEPLOYMENT_TARGET']
                if xit != 0:
                    logging.debug(
                        "Compilation-64 failed for file contents\n---\n%s\n---",
                        contents)

                    # Some datatypes are only available in 32-bit mode,
                    # such as type 'Movie' (found in QTKit/Quicktime). Not sure
                    # if this will change before Leopard is out. 
                    # Just ignore compile errors for now...
                    data64 = ''
                    #if allowFailure:
                    #    data64 = ''
                    #else:
                    #    raise RuntimeError("Compilation-64 failed")

                else:
                    data64 = subprocess.Popen(['./_scanframework'], 
                            stdout=subprocess.PIPE).communicate()[0]
                    data64 = data64.strip()

            if ppcAndIntel:
                if sys.byteorder != 'little':
                    raise RuntimeError("Sorry, must run on an intel system")

                xit = subprocess.call(
                    'cc -arch ppc -pipe -o _scanframework _scanframework.m %s 2>/dev/null'%(
                    self.CFLAGS), shell=True)
                if xit != 0:
                    os.environ['MACOSX_DEPLOYMENT_TARGET']='10.5'
                    xit = subprocess.call(
                        'cc -arch ppc -pipe -o _scanframework _scanframework.m %s 2>/dev/null'%(
                        self.CFLAGS), shell=True)
                    del os.environ['MACOSX_DEPLOYMENT_TARGET']
                if xit != 0:
                    logging.debug(
                        "Compilation-PPC failed for file contents\n---\n%s\n---",
                        contents)

                    if allowFailure:
                        dataPPC = ''
                    else:
                        raise RuntimeError("Compilation-PPC failed")

                else:
                    dataPPC = subprocess.Popen(['./_scanframework'], 
                            stdout=subprocess.PIPE).communicate()[0]
                    dataPPC = dataPPC.strip()

                return data, data64, dataPPC

        finally:
            if os.path.exists('_scanframework'):
                os.unlink('_scanframework')

            if os.path.exists('_scanframework.m'):
                os.unlink('_scanframework.m')




        return data, data64


def _main():
    
    #  Handle command-line arguments.
    parser = optparse.OptionParser(version="%prog 0.1")
    parser.add_option("-f", "--framework", dest="framework", 
        metavar="FRAMEWORK",
        help="Extra metadata from the given framework") 
    parser.add_option("-o", "--output", dest="outfile", metavar="FILE",
        help="Write the metadata to the given file (defaults to stdout)")
    parser.add_option("-O", "--output-exception", dest="outmeta", metavar="FILE",
        help="Write the exception to the given file (defaults to stdout)")
    parser.add_option("-e", "--exception", dest="exceptionfile", metavar="FILE",
        help="Use the given exception file when generating the output")
    parser.add_option("-F", "--format", dest="format", metavar="FORMAT",
        help="Select metadata format ('final-md' (default) or 'excp-templ-md')",
        default="final-md", 
        type="choice", choices=('final-md', 'excp-templ-md', 'both'))
    parser.add_option("--type_cache", dest="typecache", metavar="FILE")
    #parser.add_option("-c", "--compiler-flags", dest="cflags", metavar="CFLAGS",
    #    help="Specify custom compiler flags")
    parser.add_option("-v", "--verbose", action="store_true", dest="verbose", 
        default=False, help="Be more verbose during generation")
    parser.add_option("-d", "--dependencies", dest="dependencies_file",
        help="Write a list of dependency to the specified file " +
             "(use '-' for stdout)", metavar="FILE")

    options, args = parser.parse_args()
    if args:
        parser.error("incorrect number of arguments")

    if options.framework is None:
        parser.error("You must specify the framework to parse")

    logging.basicConfig(
        format="%(levelname)s: %(message)s",
        stream=sys.stderr,
        level=(
            {
                True: logging.DEBUG, 
                False:logging.WARNING
            }[options.verbose])
    )

    
    if options.outfile is None:
        outfp = sys.stdout

    else:
        outfp = open(options.outfile + '~', 'w')

    try:
        types = types64 = None
        if options.typecache:
            if os.path.exists(options.typecache):
                fp = open(options.typecache, 'rb')
                try:
                    types, types64 = pickle.load(fp)
                except:
                    pass

        meta = FrameworkMetadata(options.framework, types, types64)

        if options.exceptionfile:
            meta.loadExceptionsXML(options.exceptionfile)

        meta.scan()
        #meta.scanClasses()

        if options.format == 'final-md':
            meta.emitMetaDataXML(outfp)

        elif options.format == 'excp-templ-md':
            meta.emitExceptionsXML(outfp)

        elif options.format == 'both':
            meta.emitMetaDataXML(outfp)

        outfp.close()
        if options.outfile:
            os.rename(options.outfile + '~', options.outfile)

        if options.format == 'both':

            if options.outmeta is None:
                outfp = sys.stdout

            else:
                outfp = open(options.outmeta + '~', 'w')

            meta.emitExceptionsXML(outfp)

            if options.outmeta:
                os.rename(options.outmeta + '~', options.outmeta)

    except:
        if options.outfile is not None:
            if os.path.exists(options.outfile + '~'):
                os.unlink(options.outfile + '~')

        if options.outmeta is not None:
            if os.path.exists(options.outmeta + '~'):
                os.unlink(options.outmeta + '~')
        raise

    if options.dependencies_file:
        if options.dependencies_file == '-':
            fp = sys.stdout
        else:
            fp = open(dependencies_file, 'w')
        meta.emitDependecyList(fp)
        del fp

    if options.typecache:
        types, types64 = meta.types, meta.types64
        fp = open(options.typecache, 'wb')
        pickle.dump((types, types64), fp)
        fp.close()


def main():
    # Some frameworks, such as SyncServices contain some code that will only
    # work when the deployment target is at least 10.5...
    #os.environ['MACOSX_DEPLOYMENT_TARGET']='10.5'

    try:
        _main()

    except KeyboardInterrupt:
        print "Interrupted"

    except:
        raise

    return 0


if __name__ == "__main__":
    main()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.