Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(454)

Side by Side Diff: build/android/gyp/generate_v14_overlay_resources.py

Issue 14812015: [Android] Auto-generate only necessary v14 resources. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comment update. Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright 2013 The Chromium Authors. All rights reserved. 3 # Copyright 2013 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """Convert Android xml resources to API 14 compatible. 7 """Convert Android xml resources to API 14 compatible.
8 8
9 There are two reasons that we cannot just use API attributes, 9 There are two reasons that we cannot just use API 17 attributes,
10 so we are generating another set of resources by this script. 10 so we are generating another set of resources by this script.
11 11
12 1. paddingStart attribute can cause a crash on Galaxy Tab 2. 12 1. paddingStart attribute can cause a crash on Galaxy Tab 2.
13 2. There is a bug that paddingStart does not override paddingLeft on 13 2. There is a bug that paddingStart does not override paddingLeft on
14 JB-MR1. This is fixed on JB-MR2. 14 JB-MR1. This is fixed on JB-MR2.
15 15
16 Therefore, this resource generation script can be removed when 16 Therefore, this resource generation script can be removed when
17 we drop the support for JB-MR1. 17 we drop the support for JB-MR1.
18 18
19 Please refer to http://crbug.com/235118 for the details. 19 Please refer to http://crbug.com/235118 for the details.
20 """ 20 """
21 21
22 import optparse 22 import optparse
23 import os 23 import os
24 import re 24 import shutil
25 import sys 25 import sys
26 import xml.dom.minidom as minidom 26 import xml.dom.minidom as minidom
27 27
28 from util import build_utils 28 from util import build_utils
29 29
30 # Note that we are assuming 'android:' is an alias of
31 # the namespace 'http://schemas.android.com/apk/res/android'.
30 32
31 ATTRIBUTE_NAMESPACE = 'http://schemas.android.com/apk/res/android' 33 GRAVITY_ATTRIBUTES = ('android:gravity', 'android:layout_gravity')
32 34
33 # Almost all the attributes that has "Start" or "End" in 35 # Almost all the attributes that has "Start" or "End" in
34 # its name should be mapped. 36 # its name should be mapped.
35 ATTRIBUTES_TO_MAP = {'paddingStart' : 'paddingLeft', 37 ATTRIBUTES_TO_MAP = {'paddingStart' : 'paddingLeft',
36 'drawableStart' : 'drawableLeft', 38 'drawableStart' : 'drawableLeft',
37 'layout_alignStart' : 'layout_alignLeft', 39 'layout_alignStart' : 'layout_alignLeft',
38 'layout_marginStart' : 'layout_marginLeft', 40 'layout_marginStart' : 'layout_marginLeft',
39 'layout_alignParentStart' : 'layout_alignParentLeft', 41 'layout_alignParentStart' : 'layout_alignParentLeft',
40 'layout_toStartOf' : 'layout_toLeftOf', 42 'layout_toStartOf' : 'layout_toLeftOf',
41 'paddingEnd' : 'paddingRight', 43 'paddingEnd' : 'paddingRight',
42 'drawableEnd' : 'drawableRight', 44 'drawableEnd' : 'drawableRight',
43 'layout_alignEnd' : 'layout_alignRight', 45 'layout_alignEnd' : 'layout_alignRight',
44 'layout_marginEnd' : 'layout_marginRight', 46 'layout_marginEnd' : 'layout_marginRight',
45 'layout_alignParentEnd' : 'layout_alignParentRight', 47 'layout_alignParentEnd' : 'layout_alignParentRight',
46 'layout_toEndOf' : 'layout_toRightOf'} 48 'layout_toEndOf' : 'layout_toRightOf'}
47 49
48 ATTRIBUTES_TO_MAP_NS = {} 50 ATTRIBUTES_TO_MAP = dict(['android:' + k, 'android:' + v] for k, v
51 in ATTRIBUTES_TO_MAP.iteritems())
49 52
50 for k, v in ATTRIBUTES_TO_MAP.items(): 53 ATTRIBUTES_TO_MAP_REVERSED = dict([v,k] for k, v
51 ATTRIBUTES_TO_MAP_NS[(ATTRIBUTE_NAMESPACE, k)] = (ATTRIBUTE_NAMESPACE, v) 54 in ATTRIBUTES_TO_MAP.iteritems())
52
53 ATTRIBUTES_TO_MAP_NS_VALUES = set(ATTRIBUTES_TO_MAP_NS.values())
54 55
55 56
56 def IterateXmlElements(node): 57 def IterateXmlElements(node):
57 """minidom helper function that iterates all the element nodes. 58 """minidom helper function that iterates all the element nodes.
58 Iteration order is pre-order depth-first.""" 59 Iteration order is pre-order depth-first."""
59 if node.nodeType == node.ELEMENT_NODE: 60 if node.nodeType == node.ELEMENT_NODE:
60 yield node 61 yield node
61 for child_node in node.childNodes: 62 for child_node in node.childNodes:
62 for child_node_element in IterateXmlElements(child_node): 63 for child_node_element in IterateXmlElements(child_node):
63 yield child_node_element 64 yield child_node_element
64 65
65 66
66 def GenerateV14StyleResource(dom, output_file): 67 def WarnDeprecatedAttribute(name, value, filename):
newt (away) 2013/05/10 17:10:01 I'd change this function name to "WarnIfDeprecated
Kibeom Kim (inactive) 2013/05/10 17:46:14 Done.
68 """print a warning message if the given attribute is deprecated."""
69 if name in ATTRIBUTES_TO_MAP_REVERSED:
70 print >> sys.stderr, ('warning: ' + filename + ' should use ' +
71 ATTRIBUTES_TO_MAP_REVERSED[name] +
72 ' instead of ' + name)
73 elif name in GRAVITY_ATTRIBUTES and ('left' in value or 'right' in value):
74 print >> sys.stderr, ('warning: ' + filename +
75 ' should use start/end instead of left/right for ' +
76 name)
77
78
79 def GenerateV14LayoutResource(dom, filename):
80 """Convert layout resource to API 14 compatible layout resource.
81
82 Args:
83 dom: parsed minidom object to be modified.
84 Returns:
85 True if dom is modified, False otherwise.
86 """
87 is_modified = False
88
89 # Iterate all the elements' attributes to find attributes to convert.
90 for element in IterateXmlElements(dom):
91 for name, value in list(element.attributes.items()):
92 # Convert any other API 17 Start/End attributes to Left/Right attributes.
newt (away) 2013/05/10 17:10:01 what does "other" mean? I'd remove this now.
Kibeom Kim (inactive) 2013/05/10 17:46:14 Done.
93 # For example, from paddingStart="10dp" to paddingLeft="10dp"
94 # Note: gravity attributes are not necessary to convert because
95 # start/end values are backward-compatible. Explained at
96 # https://plus.sandbox.google.com/+RomanNurik/posts/huuJd8iVVXY?e=Showroom
97 if name in ATTRIBUTES_TO_MAP:
98 element.setAttribute(ATTRIBUTES_TO_MAP[name], value)
99 del element.attributes[name]
100 is_modified = True
101 else:
102 WarnDeprecatedAttribute(name, value, filename)
103
104 return is_modified
105
106
107 def GenerateV14StyleResource(dom, filename):
67 """Convert style resource to API 14 compatible style resource. 108 """Convert style resource to API 14 compatible style resource.
68 109
69 It's mostly a simple replacement, s/Start/Left s/End/Right, 110 Args:
70 on the attribute names specified by <item> element. 111 dom: parsed minidom object to be modified.
112 Returns:
113 True if dom is modified, False otherwise.
71 """ 114 """
115 is_modified = False
116
72 for style_element in dom.getElementsByTagName('style'): 117 for style_element in dom.getElementsByTagName('style'):
73 for item_element in style_element.getElementsByTagName('item'): 118 for item_element in style_element.getElementsByTagName('item'):
74 namespace, name = item_element.attributes['name'].value.split(':') 119 name = item_element.attributes['name'].value
75 # Note: namespace == 'android' is not precise because 120 value = item_element.childNodes[0].nodeValue
76 # we are looking for 'http://schemas.android.com/apk/res/android' and 121 if name in ATTRIBUTES_TO_MAP:
77 # 'android' can be aliased to another name in layout xml files where 122 item_element.attributes['name'].value = ATTRIBUTES_TO_MAP[name]
78 # this style is used. e.g. xmlns:android="http://crbug.com/". 123 is_modified = True
79 if namespace == 'android' and name in ATTRIBUTES_TO_MAP: 124 else:
80 mapped_name = ATTRIBUTES_TO_MAP[name] 125 WarnDeprecatedAttribute(name, value, filename)
81 item_element.attributes['name'] = namespace + ':' + mapped_name
82 126
83 build_utils.MakeDirectory(os.path.dirname(output_file)) 127 return is_modified
84 with open(output_file, 'w') as f:
85 dom.writexml(f, '', ' ', '\n', encoding='utf-8')
86 128
87 129
88 def GenerateV14LayoutResource(input_file, output_file): 130 def GenerateV14Resource(input_filename,
newt (away) 2013/05/10 17:10:01 slightly crazy functional idea: you could pass Ge
Kibeom Kim (inactive) 2013/05/10 17:46:14 Yeah. I like what you suggested and looks more ele
newt (away) 2013/05/10 18:06:27 thanks. I agree about the decrease in readability
89 """Convert layout resource to API 14 compatible layout resource. 131 output_v14_filename,
132 output_v17_filename):
133 """Convert layout/style resource to API 14 compatible layout/style resource.
90 134
91 It's mostly a simple replacement, s/Start/Left s/End/Right, 135 It's mostly a simple replacement, s/Start/Left s/End/Right,
92 on the attribute names. 136 on the attribute names.
137 If the generated resource is identical to the original resource,
138 don't do anything. If not, write the generated resource to
139 output_v14_filename, and copy the original resource to output_v17_filename.
93 """ 140 """
94 dom = minidom.parse(input_file) 141 dom = minidom.parse(input_filename)
95 142
96 for element in IterateXmlElements(dom): 143 root_node = IterateXmlElements(dom).next()
97 all_names = element.attributes.keysNS() 144 if root_node.nodeName == 'resources':
145 is_modified = GenerateV14StyleResource(dom, input_filename)
146 else:
147 is_modified = GenerateV14LayoutResource(dom, input_filename)
98 148
99 # Iterate all the attributes to find attributes to convert. 149 if is_modified:
100 # Note that name variable is actually a tuple that has namespace and name. 150 # Write the generated resource.
101 # For example, 151 build_utils.MakeDirectory(os.path.dirname(output_v14_filename))
102 # name == ('http://schemas.android.com/apk/res/android', 'paddingStart') 152 with open(output_v14_filename, 'w') as f:
103 for name, value in list(element.attributes.itemsNS()): 153 dom.writexml(f, '', ' ', '\n', encoding='utf-8')
104 # Note: gravity attributes are not necessary to convert because
105 # start/end values are backward-compatible. Explained at
106 # https://plus.sandbox.google.com/+RomanNurik/posts/huuJd8iVVXY?e=Showroom
107 154
108 # Convert any other API 17 Start/End attributes to Left/Right attributes. 155 # Copy the original resource.
109 # For example, from paddingStart="10dp" to paddingLeft="10dp" 156 build_utils.MakeDirectory(os.path.dirname(output_v17_filename))
110 if name in ATTRIBUTES_TO_MAP_NS: 157 shutil.copy2(input_filename, output_v17_filename)
111 mapped_name = ATTRIBUTES_TO_MAP_NS[name]
112
113 # Add the new mapped attribute and remove the original attribute.
114 # For example, add paddingLeft and remove paddingStart.
115 # Note that instead of element.setAttribute(...), this is more correct.
116 # element.setAttributeNS(mapped_name[0], mapped_name[1], value)
117 # However, there is a minidom bug that doesn't print namespace set by
118 # setAttributeNS. Hence this workaround.
119 # This is a similar bug discussion about minidom namespace normalizing.
120 # http://stackoverflow.com/questions/863774/how-to-generate-xml-document s-with-namespaces-in-python
121 element.setAttribute('android:' + mapped_name[1], value)
122 del element.attributes[name]
123 elif name in ATTRIBUTES_TO_MAP_NS_VALUES:
124 # TODO(kkimlabs): Enable warning once layouts have been converted
125 # print >> sys.stderror, 'Warning: layout should use xxx instead of yyy'
126 pass
127
128 build_utils.MakeDirectory(os.path.dirname(output_file))
129 with open(output_file, 'w') as f:
130 dom.writexml(f, '', ' ', '\n', encoding='utf-8')
131 158
132 159
133 def GenerateV14XmlResourcesInDir(input_dir, output_dir, only_styles=False): 160 def GenerateV14XmlResourcesInDir(input_dir, output_v14_dir, output_v17_dir):
134 """Convert resources to API 14 compatible XML resources in the directory.""" 161 """Convert resources to API 14 compatible XML resources in the directory."""
135 for input_file in build_utils.FindInDirectory(input_dir, '*.xml'): 162 for input_filename in build_utils.FindInDirectory(input_dir, '*.xml'):
136 output_file = os.path.join(output_dir, 163 rel_filename = os.path.relpath(input_filename, input_dir)
137 os.path.relpath(input_file, input_dir)) 164 output_v14_filename = os.path.join(output_v14_dir, rel_filename)
138 if only_styles: 165 output_v17_filename = os.path.join(output_v17_dir, rel_filename)
139 dom = minidom.parse(input_file) 166 GenerateV14Resource(input_filename, output_v14_filename,
140 if not dom.getElementsByTagName('style'): 167 output_v17_filename)
141 continue
142 GenerateV14StyleResource(dom, output_file)
143 else:
144 GenerateV14LayoutResource(input_file, output_file)
145 168
146 169
147 def ParseArgs(): 170 def ParseArgs():
148 """Parses command line options. 171 """Parses command line options.
149 172
150 Returns: 173 Returns:
151 An options object as from optparse.OptionsParser.parse_args() 174 An options object as from optparse.OptionsParser.parse_args()
152 """ 175 """
153 parser = optparse.OptionParser() 176 parser = optparse.OptionParser()
154 parser.add_option('--res-dir', 177 parser.add_option('--res-dir',
155 help='directory containing resources ' 178 help='directory containing resources '
156 'used to generate v14 resources') 179 'used to generate v14 resources')
newt (away) 2013/05/10 18:06:27 be sure to update these: "v14 compatible resource
Kibeom Kim (inactive) 2013/05/10 18:42:55 Done.
157 parser.add_option('--res-v14-dir', 180 parser.add_option('--res-v14-overlay-dir',
158 help='output directory into which ' 181 help='output directory into which '
159 'v14 resources will be generated') 182 'v14 resources will be generated')
160 parser.add_option('--stamp', help='File to touch on success') 183 parser.add_option('--stamp', help='File to touch on success')
161 184
162 options, args = parser.parse_args() 185 options, args = parser.parse_args()
163 186
164 if args: 187 if args:
165 parser.error('No positional arguments should be given.') 188 parser.error('No positional arguments should be given.')
166 189
167 # Check that required options have been provided. 190 # Check that required options have been provided.
168 required_options = ('res_dir', 'res_v14_dir') 191 required_options = ('res_dir', 'res_v14_overlay_dir')
169 build_utils.CheckOptions(options, parser, required=required_options) 192 build_utils.CheckOptions(options, parser, required=required_options)
170 return options 193 return options
171 194
172 195
173 def main(argv): 196 def main(argv):
174 options = ParseArgs() 197 options = ParseArgs()
175 198
176 build_utils.DeleteDirectory(options.res_v14_dir) 199 build_utils.DeleteDirectory(options.res_v14_overlay_dir)
177 build_utils.MakeDirectory(options.res_v14_dir) 200 build_utils.MakeDirectory(options.res_v14_overlay_dir)
178 201
179 for name in os.listdir(options.res_dir): 202 for name in os.listdir(options.res_dir):
180 if not os.path.isdir(os.path.join(options.res_dir, name)): 203 if not os.path.isdir(os.path.join(options.res_dir, name)):
181 continue 204 continue
182 205
183 dir_pieces = name.split('-') 206 dir_pieces = name.split('-')
184 resource_type = dir_pieces[0] 207 resource_type = dir_pieces[0]
185 qualifiers = dir_pieces[1:] 208 qualifiers = dir_pieces[1:]
186 209
187 # Android pre-v17 API doesn't support RTL. Skip. 210 # Android pre-v17 API doesn't support RTL. Skip.
188 if 'ldrtl' in qualifiers: 211 if 'ldrtl' in qualifiers:
189 continue 212 continue
190 213
214 # We also need to copy the original v17 resource to *-v17 directory
215 # because the generated v14 resource will hide the original resource.
191 input_dir = os.path.join(options.res_dir, name) 216 input_dir = os.path.join(options.res_dir, name)
192 output_dir = os.path.join(options.res_v14_dir, name) 217 output_v14_dir = os.path.join(options.res_v14_overlay_dir, name)
218 output_v17_dir = os.path.join(options.res_v14_overlay_dir, name + '-v17')
193 219
194 # We only convert resources under layout*/, xml*/, 220 # We only convert resources under layout*/, xml*/,
195 # and style resources under values*/. 221 # and style resources under values*/.
196 # TODO(kkimlabs): don't process xml directly once all layouts have 222 # TODO(kkimlabs): don't process xml directly once all layouts have
197 # been moved out of XML directory. see http://crbug.com/238458 223 # been moved out of XML directory. see http://crbug.com/238458
198 if resource_type in ('layout', 'xml'): 224 if resource_type in ('layout', 'xml', 'values'):
199 GenerateV14XmlResourcesInDir(input_dir, output_dir) 225 GenerateV14XmlResourcesInDir(input_dir, output_v14_dir, output_v17_dir)
200 elif resource_type in ('values'):
201 GenerateV14XmlResourcesInDir(input_dir, output_dir, only_styles=True)
202 226
203 if options.stamp: 227 if options.stamp:
204 build_utils.Touch(options.stamp) 228 build_utils.Touch(options.stamp)
205 229
206 if __name__ == '__main__': 230 if __name__ == '__main__':
207 sys.exit(main(sys.argv)) 231 sys.exit(main(sys.argv))
208 232
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698