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 |