Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/python | |
| 2 | |
| 3 # Copyright (c) 2009 The Chromium Authors. All rights reserved. | |
| 4 # Use of this source code is governed by a BSD-style license that can be | |
| 5 # found in the LICENSE file. | |
| 6 | |
| 7 # Usage: make_ib_classes.py output.mm input.xib [...] | |
| 8 # | |
| 9 # Generates an Objective-C++ file at output.mm referencing each class described | |
| 10 # in input.xib. | |
| 11 # | |
| 12 # This script is useful when building an application containing .nib or .xib | |
| 13 # files that reference Objective-C classes that may not be referenced by other | |
| 14 # code in the application. The intended use case is when the .nib and .xib | |
| 15 # files refer to classes that are built into a static library that gets linked | |
| 16 # into the main executable. If nothing in the main executable references those | |
| 17 # classes, the linker will not include them in its output (without -all_load or | |
| 18 # -ObjC). Using this script, references to such classes are created, such that | |
|
Mark Mentovai
2009/02/28 00:50:12
See?
| |
| 19 # if output.mm is compiled into the application itself, it will provide the | |
| 20 # class references and cause the linker to bring the required code into the | |
| 21 # executable. | |
| 22 # | |
| 23 # If your application is structured in the above way, and you're plagued with | |
| 24 # messages like: | |
| 25 # app[12345:101] Unknown class `MyApp' in nib file, using `NSObject' instead. | |
| 26 # then this script may be right for you. | |
| 27 | |
| 28 | |
| 29 import errno | |
| 30 import os | |
| 31 import os.path | |
| 32 import re | |
| 33 import subprocess | |
| 34 import sys | |
| 35 | |
| 36 | |
| 37 # Patterns used by ListIBClasses | |
| 38 | |
| 39 # A pattern that matches the line preceding a class name. | |
| 40 _class_re = re.compile('<key>class</key>$') | |
| 41 | |
| 42 # A pattern that matches the line with a class name; match group 1 should be | |
| 43 # the class name. | |
| 44 _class_name_re = re.compile('<string>(.*)</string>$') | |
| 45 | |
| 46 # A pattern that matches class names to exclude from the output. This includes | |
| 47 # various Cocoa classes. | |
| 48 _forbidden_class_re = re.compile('^(NS|IB|FirstResponder$|WebView$)') | |
| 49 | |
| 50 def ListIBClasses(ib_path, class_names=None): | |
| 51 """Returns a list of class names referenced by ib_path. | |
| 52 | |
| 53 ib_path is a path to an Interface Builder document. It may be a .nib or a | |
| 54 .xib. | |
| 55 | |
| 56 This function calls "ibtool --classes" to get the list of class names. | |
| 57 ibtool's output is in XML plist format. Rather than doing proper structured | |
| 58 plist scanning, this function relies on the fact that plists are serialized | |
| 59 to XML in a consistent way, and simply takes the string value names of any | |
| 60 dictionary key named "class" as class names. | |
| 61 | |
| 62 class_names may be specified as an existing list to use. This is helpful | |
| 63 when this function will be called several times for multiple nib/xib files. | |
| 64 """ | |
| 65 if class_names == None: | |
| 66 class_names = [] | |
| 67 | |
| 68 # When running within an Xcode build, use the tools from that Xcode | |
| 69 # installation. | |
| 70 developer_tools_dir = os.getenv('DEVELOPER_BIN_DIR', '/usr/bin') | |
| 71 ibtool_path = os.path.join(developer_tools_dir, 'ibtool') | |
| 72 ibtool_command = [ibtool_path, '--classes', ib_path] | |
| 73 | |
| 74 ibtool = subprocess.Popen(ibtool_command, stdout=subprocess.PIPE) | |
| 75 | |
| 76 ibtool_output = ibtool.communicate()[0] | |
| 77 | |
| 78 ibtool_rv = ibtool.returncode | |
| 79 assert ibtool_rv == 0 | |
| 80 | |
| 81 # Loop through the output, looking for "class" keys. The string value on | |
| 82 # any line following a "class" key is taken as a class name. | |
| 83 is_class_name = False | |
| 84 for line in ibtool_output.splitlines(): | |
| 85 if is_class_name: | |
| 86 class_name = _class_name_re.search(line).group(1) | |
| 87 is_class_name = False | |
| 88 if not class_name in class_names and \ | |
| 89 not _forbidden_class_re.search(class_name): | |
| 90 class_names.append(class_name) | |
| 91 elif _class_re.search(line): | |
| 92 is_class_name = True | |
| 93 | |
| 94 return class_names | |
| 95 | |
| 96 | |
| 97 def main(args): | |
| 98 assert len(args) > 2 | |
| 99 (script_path, output_path) = args[0:2] | |
| 100 assert output_path.endswith('.mm') | |
| 101 input_paths = args[2:] | |
| 102 | |
| 103 try: | |
| 104 os.unlink(output_path) | |
| 105 except OSError, e: | |
| 106 if e.errno != errno.ENOENT: | |
| 107 raise | |
| 108 | |
| 109 class_names = [] | |
| 110 | |
| 111 # Get the class names for all desired files. | |
| 112 for input_path in input_paths: | |
| 113 ListIBClasses(input_path, class_names) | |
| 114 | |
| 115 class_names.sort() | |
| 116 | |
| 117 # Write the requested output file. Each class is referenced simply by | |
| 118 # calling its +class function. In order to do this, each class needs a | |
| 119 # bogus @interface to tell the compiler that it's an NSObject subclass. | |
| 120 # #import NSObject.h to get the definition of NSObject without bringing in | |
| 121 # other headers that might provide real declarations. | |
| 122 | |
| 123 output_file = open(output_path, 'w') | |
| 124 print >>output_file, \ | |
| 125 """// This file was generated by %s. Do not edit. | |
| 126 | |
| 127 #import <Foundation/NSObject.h> | |
| 128 """ % os.path.basename(script_path) | |
| 129 | |
| 130 for class_name in class_names: | |
| 131 print >>output_file, '@interface %s : NSObject\n@end' % class_name | |
| 132 | |
| 133 print >>output_file, '\nnamespace {\n\nvoid IBClasses() {' | |
| 134 | |
| 135 for class_name in class_names: | |
| 136 print >>output_file, ' [%s class];' % class_name | |
| 137 | |
| 138 print >>output_file, '}\n\n} // namespace' | |
| 139 | |
| 140 output_file.close() | |
| 141 | |
| 142 return 0 | |
| 143 | |
| 144 | |
| 145 if __name__ == '__main__': | |
| 146 sys.exit(main(sys.argv)) | |
| OLD | NEW |