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

Side by Side Diff: platform_tools/android/bin/gyp_to_android.py

Issue 140503007: Scripts to generate Android.mk for framework Skia. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Respond to comments in patch set 5. Created 6 years, 11 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
OLDNEW
(Empty)
1 #!/usr/bin/python
2
3 # Copyright 2014 Google Inc.
4 #
5 # Use of this source code is governed by a BSD-style license that can be
6 # found in the LICENSE file.
7
8 """
9 Script for generating the Android framework's verision of Skia from gyp
epoger 2014/01/23 18:57:58 No need to indent these lines. verision -> versio
scroggo 2014/01/24 20:09:02 Done.
10 files.
11 """
12
13 import os
14
15 # Folder containing all gyp files and generated gypd files.
16 GYP_FOLDER = 'gyp'
17
18 # FIXME: Currently the x86 specific files are not included. Include them.
epoger 2014/01/23 18:57:58 I think we more commonly use TODO(username)
scroggo 2014/01/24 20:09:02 Done.
19 INCLUDE_X86_OPTS = False
20
21 def WriteGroup(f, name, items, append):
epoger 2014/01/23 18:57:58 Throughout: http://google-styleguide.googlecode.co
scroggo 2014/01/24 20:09:02 I would swear I found some style guide that told m
scroggo 2014/01/31 22:16:24 And thanks to Joe I now see where I found that: ht
22 """
23 Helper function to list all names passed to a variable.
24 @param f File open for writing (Android.mk)
25 @param name Name of the makefile variable (e.g. LOCAL_CFLAGS)
26 @param items list of strings to be passed to the variable.
27 @param append Whether to append to the variable or overwrite it.
28 """
29 if len(items) == 0:
epoger 2014/01/23 18:57:58 mild preference for "if not items:", which will ha
scroggo 2014/01/24 20:09:02 That is clearer, I think. Done.
30 return
31 if append:
32 f.write('%s += \\\n' % name)
33 else:
34 f.write('%s := \\\n' % name)
35 for item in items:
36 f.write('\t%s \\\n' % item)
epoger 2014/01/23 18:57:58 There's probably some easy way to make python do t
scroggo 2014/01/24 20:09:02 I think the choice between '+' and ':' is clearer
37 # Add an extra blank line since the last line ended in a \
38 f.write('\n')
39
40 class VarsDict(dict):
41 """
42 Custom dict of lists for each makefile variable. Does not allow insertion of
epoger 2014/01/23 18:57:58 I think the Python-y way of doing this is http://d
scroggo 2014/01/24 20:09:02 Added a TODO. Will think on it.
scroggo 2014/01/31 22:16:24 Patch set 13 uses a namedtuple. It has some downsi
43 new keys.
44 """
45 def __init__(self):
46 dict.__init__(self)
47 self.__allow_insert = True
48 self['cflags'] = []
49 self['cppflags'] = []
epoger 2014/01/23 18:57:58 Are these truly lists (ordered, allowing multiple
scroggo 2014/01/24 20:09:02 Thanks for the tip. They're actually neither - the
scroggo 2014/01/31 22:16:24 Now they are OrderedSets. I don't think it was so
50 self['defines'] = []
51 self['sources'] = []
52 self['shared_libraries'] = []
53 self['static_libraries'] = []
54 self['include_dirs'] = []
55 self['export_include_dirs'] = []
56 self['known_targets'] = []
57 self.__allow_insert = False
58 self.__allow_convert = True
59
60 def __setitem__(self, k, v):
61 # Prevent the insertion of arbitrary keys.
62 assert self.__allow_insert
63 dict.__setitem__(self, k, v)
64
65 def ConvertDefines(self):
66 """
67 Add each string in 'defines' to 'cflags', prepended with '-D'. Can only
epoger 2014/01/23 18:57:58 Alternatively, I guess you could add a GetCflags()
scroggo 2014/01/24 20:09:02 Good thinking. I could also add defines directly t
68 be done once.
69 """
70 assert self.__allow_convert
71 self.__allow_convert = False
72 for define in self['defines']:
73 define = '-D' + define
epoger 2014/01/23 18:57:58 I think modifying the iterator is dangerous/confus
scroggo 2014/01/24 20:09:02 Good point. I'll add that, and just know that Conv
scroggo 2014/01/31 22:16:24 As hinted at previously, patch set 13 eliminates C
74 self['cflags'].append(define)
75
76 def WriteLocalVars(f, var_dict, append):
77 """
78 Helper function to write all the members of var_dict to the makefile.
79 @param f File open for writing (Android.mk)
80 @param var_dict VarsDict holding the unique values for one configuration.
81 @param append Whether to append to each makefile variable or overwrite it.
82 """
83 var_dict.ConvertDefines()
84 # Always append LOCAL_CFLAGS. This allows us to define some early on in the
85 # makefile and not overwrite them.
86 WriteGroup(f, 'LOCAL_CFLAGS', var_dict['cflags'], True)
epoger 2014/01/23 18:57:58 Maybe we should just use 'LOCAL_CFLAGS' as the key
scroggo 2014/01/24 20:09:02 I could potentially take that further and pass the
scroggo 2014/01/31 22:16:24 Done.
87 WriteGroup(f, 'LOCAL_CPPFLAGS', var_dict['cppflags'], append)
88 WriteGroup(f, 'LOCAL_SRC_FILES', var_dict['sources'], append)
89 WriteGroup(f, 'LOCAL_SHARED_LIBRARIES', var_dict['shared_libraries'], append)
90 WriteGroup(f, 'LOCAL_STATIC_LIBRARIES', var_dict['static_libraries'], append)
91 WriteGroup(f, 'LOCAL_C_INCLUDES', var_dict['include_dirs'], append)
92 WriteGroup(f, 'LOCAL_EXPORT_C_INCLUDE_DIRS', var_dict['export_include_dirs'],
93 append)
94
95 AUTOGEN_WARNING = (
96 """
97 ###############################################################################
98 #
99 # THIS FILE IS AUTOGENERATED BY GYP_TO_ANDROID.PY. DO NOT EDIT.
100 #
101 ###############################################################################
102
103 """
104 )
105
106 DEBUGGING_HELP = (
107 """
108 ###############################################################################
109 #
110 # PROBLEMS WITH SKIA DEBUGGING?? READ THIS...
111 #
112 # The debug build results in changes to the Skia headers. This means that those
113 # using libskia must also be built with the debug version of the Skia headers.
114 # There are a few scenarios where this comes into play:
115 #
116 # (1) You're building debug code that depends on libskia.
117 # (a) If libskia is built in release, then define SK_RELEASE when building
118 # your sources.
119 # (b) If libskia is built with debugging (see step 2), then no changes are
120 # needed since your sources and libskia have been built with SK_DEBUG.
121 # (2) You're building libskia in debug mode.
122 # (a) RECOMMENDED: You can build the entire system in debug mode. Do this by
123 # updating your build/config.mk to include -DSK_DEBUG on the line that
124 # defines COMMON_GLOBAL_CFLAGS
125 # (b) You can update all the users of libskia to define SK_DEBUG when they are
126 # building their sources.
127 #
128 # NOTE: If neither SK_DEBUG or SK_RELEASE are defined then Skia checks NDEBUG to
129 # determine which build type to use.
130 ###############################################################################
131
132 """
133 )
134
135 def WriteAndroidMk(common, arm, armNeon, x86, default):
136 """
137 Given all the variables, write the final make file.
138 @param common VarsDict holding variables definitions common to all
139 configurations.
140 @param arm VarsDict holding variable definitions unique to arm. Will be
141 written to the makefile inside an 'ifeq ($(TARGET_ARCH), arm)'
142 block.
143 @param armNeon VarsDict holding variable definitions unique to arm with neon.
144 Will be written inside an 'ifeq ($(ARCH_ARM_HAVE_NEON),true)'
145 block nested inside an 'ifeq ($(TARGET_ARCH), arm)' block.
146 @param x86 VarsDict holding variable definitions unique to x86. Will be
147 written inside an 'ifeq ($(TARGET_ARCH),x86)' block.
148 @param default VarsDict holding variable definitions for an architecture
149 without custom optimizations.
150 TODO: Add mips.
151 """
152 with open('Android.mk', 'w') as f:
153 f.write(AUTOGEN_WARNING)
154 f.write('BASE_PATH := $(call my-dir)\n')
155 f.write('LOCAL_PATH:= $(call my-dir)\n')
156
157 f.write(DEBUGGING_HELP)
158
159 f.write('include $(CLEAR_VARS)\n')
160
161 f.write('LOCAL_ARM_MODE := thumb\n')
162
163 # need a flag to tell the C side when we're on devices with large memory
164 # budgets (i.e. larger than the low-end devices that initially shipped)
165 f.write('ifeq ($(ARCH_ARM_HAVE_VFP),true)\n')
166 f.write('\tLOCAL_CFLAGS += -DANDROID_LARGE_MEMORY_DEVICE\n')
167 f.write('endif\n\n')
168
169 f.write('ifeq ($(TARGET_ARCH),x86)\n')
170 f.write('\tLOCAL_CFLAGS += -DANDROID_LARGE_MEMORY_DEVICE\n')
171 f.write('endif\n\n')
172
173 f.write('# used for testing\n')
174 f.write('#LOCAL_CFLAGS += -g -O0\n\n')
175
176 f.write('ifeq ($(NO_FALLBACK_FONT),true)\n')
177 f.write('\tLOCAL_CFLAGS += -DNO_FALLBACK_FONT\n')
178 f.write('endif\n\n')
179
180 WriteLocalVars(f, common, False)
181
182 f.write('ifeq ($(TARGET_ARCH),arm)\n')
183 f.write('ifeq ($(ARCH_ARM_HAVE_NEON),true)\n')
184 WriteLocalVars(f, armNeon, True)
185 f.write('endif\n\n')
186 WriteLocalVars(f, arm, True)
187
188 if INCLUDE_X86_OPTS:
189 f.write('else ifeq ($(TARGET_ARCH),x86)\n')
190 WriteLocalVars(f, x86, True)
191
192 f.write('else\n')
193 WriteLocalVars(f, default, True)
194 f.write('endif\n\n')
195
196 f.write('include external/stlport/libstlport.mk\n')
197 f.write('LOCAL_MODULE:= libskia\n')
198 f.write('include $(BUILD_SHARED_LIBRARY)\n')
199
200 def AddToList(li, item):
201 """
202 Add item to li, but only if it is not already in li.
epoger 2014/01/23 18:57:58 sounds like a set, not a list
scroggo 2014/01/24 20:09:02 As stated above, I do care about order.
203 """
204 if item not in li:
205 li.append(item)
206
207 def GetMembers(d, name):
epoger 2014/01/23 18:57:58 I think this function can be replaced by calls to
scroggo 2014/01/24 20:09:02 Done. Thanks!
208 """
209 Get the list corresponding to name in dictionary d.
210 """
211 if not name in d:
212 return []
213 return d[name]
214
215 def ParseDictionary(var_dict, d, current_target_name):
216 """
217 Helper function to get the meaningful entries in a dictionary.
218 @param var_dict VarsDict object for storing the results of the parsing.
219 @param d Dictionary object to parse.
220 @param current_target_name The current target being parsed. If this
221 dictionary is a target, this will be its entry
222 'target_name'. Otherwise, this will be the name of
223 the target which contains this dictionary.
224 """
225 for source in GetMembers(d, 'sources'):
226 if source.endswith('.h'):
227 # Android.mk does not need the header files.
228 continue
229 if source.endswith('gypi'):
230 # The gypi files are included in sources, but the sources they included
231 # are also included. No need to parse them again.
232 continue
233 # The path is relative to the gyp folder, but Android wants the path
234 # relative to the root.
235 source = source.replace('../src', 'src', 1)
236 AddToList(var_dict['sources'], source)
237
238 for lib in GetMembers(d, 'libraries'):
239 if lib.endswith('.a'):
240 # Remove the '.a'
241 lib = lib[:-2]
242 # Add 'lib', if necessary
243 if not lib.startswith('lib'):
244 lib = 'lib' + lib
245 AddToList(var_dict['static_libraries'], lib)
246 else:
247 # lib will be in the form of '-l<name>'. Change it to 'lib<name>'
248 lib = lib.replace('-l', 'lib', 1)
249 AddToList(var_dict['shared_libraries'], lib)
250
251 for dependency in GetMembers(d, 'dependencies'):
252 # Each dependency is listed as
253 # <path_to_file>:<target>#target
254 li = dependency.split(':')
255 assert(len(li) <= 2 and len(li) >= 1)
256 sub_targets = []
257 if len(li) == 2 and li[1] != '*':
258 sub_targets.append(li[1].split('#')[0])
259 sub_path = li[0]
260 assert(sub_path.endswith('.gyp'))
261 # Although the original reference is to a .gyp, parse the corresponding
262 # gypd file, which was constructed by gyp.
263 sub_path = sub_path + 'd'
264 ParseGypd(var_dict, sub_path, sub_targets)
265
266 if 'default_configuration' in d:
267 config_name = d['default_configuration']
268 # default_configuration is meaningless without configurations
269 assert('configurations' in d)
270 config = d['configurations'][config_name]
271 ParseDictionary(var_dict, config, current_target_name)
272
273 for flag in GetMembers(d, 'cflags'):
274 AddToList(var_dict['cflags'], flag)
275 for flag in GetMembers(d, 'cflags_cc'):
276 AddToList(var_dict['cppflags'], flag)
277
278 for include in GetMembers(d, 'include_dirs'):
279 # The input path will be relative to gyp/, but Android wants relative to
280 # LOCAL_PATH
281 include = include.replace('..', '$(LOCAL_PATH)', 1)
282 # Remove a trailing slash, if present.
283 if include.endswith('/'):
284 include = include[:-1]
285 AddToList(var_dict['include_dirs'], include)
286 # For the top level, libskia, include directories should be exported.
287 if current_target_name == 'libskia':
288 AddToList(var_dict['export_include_dirs'], include)
289
290 for define in GetMembers(d, 'defines'):
291 AddToList(var_dict['defines'], define)
292
293 def ParseGypd(var_dict, path, desired_targets=None):
294 """
295 Parse a gypd file.
296 @param var_dict VarsDict object for storing the result of the parse.
297 @param path Path to gypd file.
298 @param desired_targets List of targets to be parsed from this file. If empty,
299 parse all targets.
300 """
301 d = {}
302 with open(path, 'r') as f:
303 # Read the entire file as a dictionary
304 d = eval(f.read())
305 # The gypd file is structured such that the top level dictionary has an entry
306 # named 'targets'
307 if 'targets' not in d:
308 return
309 targets = d['targets']
310 for target in targets:
311 target_name = target['target_name']
312 if target_name in var_dict['known_targets']:
313 # Avoid circular dependencies
314 continue
315 if desired_targets and target_name not in desired_targets:
316 # Our caller does not depend on this one
317 continue
318 # Add it to our known targets so we don't parse it again
319 var_dict['known_targets'].append(target_name)
320
321 ParseDictionary(var_dict, target, target_name)
322
323 def Intersect(var_dict_list):
epoger 2014/01/23 18:57:58 I think the VarsDict class, and functions that ope
scroggo 2014/01/24 20:09:02 Added a TODO. Will do in a followup patch set.
scroggo 2014/01/31 22:16:24 Done.
324 """
325 Find the intersection of a list of VarsDicts and trim each input to its
326 unique entries.
327 @param var_dict_list list of VarsDicts.
328 @return VarsDict containing list entries common to all VarsDicts in
329 var_dict_list
330 """
331 intersection = VarsDict()
332 # First VarsDict
333 var_dictA = var_dict_list[0]
334 # The rest.
335 otherVarDicts = var_dict_list[1:]
336
337 for key in var_dictA.keys():
338 # Copy A's list, so we can continue iterating after modifying the original.
339 aList = list(var_dictA[key])
340 for item in aList:
341 # If item is in all lists, add to intersection, and remove from all.
342 in_all_lists = True
343 for var_dict in otherVarDicts:
344 # Each dictionary must be a VarsDict, which always contains the same
345 # set of keys.
346 if not item in var_dict[key]:
347 in_all_lists = False
348 break
349 if in_all_lists:
350 intersection[key].append(item)
351 for var_dict in var_dict_list:
352 var_dict[key].remove(item)
353 return intersection
354
355 def CleanGypdFiles():
356 """
357 Remove the gypd files generated by android_framework_gyp.main().
358 """
359 assert os.path.isdir(GYP_FOLDER)
360 files = os.listdir(GYP_FOLDER)
361 for f in files:
362 if f.endswith('gypd'):
363 os.remove(os.path.join(GYP_FOLDER, f))
364
365 import android_framework_gyp
366
367 def main():
368 # Move up to top of trunk
369 script_dir = os.path.dirname(__file__)
370 skia_dir = os.path.normpath(os.path.join(script_dir, os.pardir, os.pardir,
371 os.pardir))
372 os.chdir(skia_dir)
373
374 main_gypd_file = os.path.join(GYP_FOLDER, 'android_framework_lib.gypd')
375
376 print 'Creating Android.mk',
377
378 # Call the script to generate gypd files, first using a non-existant archtype.
379 android_framework_gyp.main('other', False)
380 # And store its variables in the default dict
381 default_var_dict = VarsDict()
382 ParseGypd(default_var_dict, main_gypd_file)
383 CleanGypdFiles()
384
385 print '.',
386
387 # Now do the same for Arm. arm_var_dict will contain all the variable
388 # definitions needed to build with arm (meaning that it will share most
389 # with default_var_dict).
390 android_framework_gyp.main('arm', False)
391 arm_var_dict = VarsDict()
392 ParseGypd(arm_var_dict, main_gypd_file)
393 CleanGypdFiles()
394
395 print '.',
396
397 # Now do the same for Arm/Neon.
398 android_framework_gyp.main('arm', True)
399 arm_neon_var_dict = VarsDict()
400 ParseGypd(arm_neon_var_dict, main_gypd_file)
401 CleanGypdFiles()
402
403 print '.'
404
405 # Now do the same for x86.
406 x86_var_dict = VarsDict()
407 if INCLUDE_X86_OPTS:
408 android_framework_gyp.main('x86', False)
409 ParseGypd(x86_var_dict, main_gypd_file)
410 CleanGypdFiles()
411
412 # Compute the intersection of all targets. All the files in the intersection
413 # should be part of the makefile always. Each dict will now contain trimmed
414 # lists containing only variable definitions specific to that configuration.
415 var_dict_list = [default_var_dict, arm_var_dict, arm_neon_var_dict]
416 if INCLUDE_X86_OPTS:
417 var_dict_list.append(x86_var_dict)
418 common = Intersect(var_dict_list)
419
420 # Further trim arm_neon_var_dict with arm_var_dict. After this call,
421 # arm_var_dict (which will now be the intersection) includes all definitions
422 # used by both arm and arm + neon, and arm_neon_var_dict will only contain
423 # those specific to arm + neon.
424 arm_var_dict = Intersect([arm_var_dict, arm_neon_var_dict])
425
426 WriteAndroidMk(common, arm_var_dict, arm_neon_var_dict, x86_var_dict,
427 default_var_dict)
428
429 if __name__ == '__main__':
430 main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698