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

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 3. 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
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.
19 INCLUDE_X86_OPTS = False
20
21 def WriteGroup(f, name, items, append):
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:
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)
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
43 new keys.
44 """
45 def __init__(self):
46 dict.__init__(self)
47 self.__allow_insert = True
48 self['cflags'] = []
49 self['cppflags'] = []
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
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
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)
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 DEBUGGING_HELP = (
96 """
97 ###############################################################################
djsollen 2014/01/22 21:02:44 we should also include now that this file is auto-
scroggo 2014/01/22 23:52:41 Done.
98 #
99 # PROBLEMS WITH SKIA DEBUGGING?? READ THIS...
100 #
101 # The debug build results in changes to the Skia headers. This means that those
102 # using libskia must also be built with the debug version of the Skia headers.
103 # There are a few scenarios where this comes into play:
104 #
105 # (1) You're building debug code that depends on libskia.
106 # (a) If libskia is built in release, then define SK_RELEASE when building
107 # your sources.
108 # (b) If libskia is built with debugging (see step 2), then no changes are
109 # needed since your sources and libskia have been built with SK_DEBUG.
110 # (2) You're building libskia in debug mode.
111 # (a) RECOMMENDED: You can build the entire system in debug mode. Do this by
112 # updating your build/config.mk to include -DSK_DEBUG on the line that
113 # defines COMMON_GLOBAL_CFLAGS
114 # (b) You can update all the users of libskia to define SK_DEBUG when they are
115 # building their sources.
116 #
117 # NOTE: If neither SK_DEBUG or SK_RELEASE are defined then Skia checks NDEBUG to
118 # determine which build type to use.
119 ###############################################################################
120
121 """
122 )
123
124 def WriteAndroidMk(common, arm, armNeon, x86, default):
125 """
126 Given all the variables, write the final make file.
127 @param common VarsDict holding variables definitions common to all
128 configurations.
129 @param arm VarsDict holding variable definitions unique to arm. Will be
130 written to the makefile inside an 'ifeq ($(TARGET_ARCH), arm)'
131 block.
132 @param armNeon VarsDict holding variable definitions unique to arm with neon.
133 Will be written inside an 'ifeq ($(ARCH_ARM_HAVE_NEON),true)'
134 block nested inside an 'ifeq ($(TARGET_ARCH), arm)' block.
135 @param x86 VarsDict holding variable definitions unique to x86. Will be
136 written inside an 'ifeq ($(TARGET_ARCH),x86)' block.
137 @param default VarsDict holding variable definitions for an architecture
138 without custom optimizations.
139 TODO: Add mips.
140 """
141 with open('Android.mk', 'w') as f:
142 f.write('BASE_PATH := $(call my-dir)\n')
143 f.write('LOCAL_PATH:= $(call my-dir)\n')
144
145 f.write(DEBUGGING_HELP)
146
147 f.write('include $(CLEAR_VARS)\n')
148
149 f.write('LOCAL_ARM_MODE := thumb\n')
150
151 # need a flag to tell the C side when we're on devices with large memory
152 # budgets (i.e. larger than the low-end devices that initially shipped)
153 f.write('ifeq ($(ARCH_ARM_HAVE_VFP),true)\n')
154 f.write('\tLOCAL_CFLAGS += -DANDROID_LARGE_MEMORY_DEVICE\n')
155 f.write('endif\n\n')
156
157 f.write('ifeq ($(TARGET_ARCH),x86)\n')
158 f.write('\tLOCAL_CFLAGS += -DANDROID_LARGE_MEMORY_DEVICE\n')
159 f.write('endif\n\n')
160
161 f.write('# used for testing\n')
162 f.write('#LOCAL_CFLAGS += -g -O0\n\n')
163
164 f.write('ifeq ($(NO_FALLBACK_FONT),true)\n')
165 f.write('\tLOCAL_CFLAGS += -DNO_FALLBACK_FONT\n')
166 f.write('endif\n\n')
167
168 WriteLocalVars(f, common, False)
169
170 f.write('ifeq ($(TARGET_ARCH),arm)\n')
171 f.write('ifeq ($(ARCH_ARM_HAVE_NEON),true)\n')
172 WriteLocalVars(f, armNeon, True)
173 f.write('endif\n\n')
174 WriteLocalVars(f, arm, True)
175
176 if INCLUDE_X86_OPTS:
177 f.write('else ifeq ($(TARGET_ARCH),x86)\n')
178 WriteLocalVars(f, x86, True)
179
180 f.write('else\n')
181 WriteLocalVars(f, default, True)
182 f.write('endif\n\n')
183
184 f.write('include external/stlport/libstlport.mk\n')
185 f.write('LOCAL_MODULE:= libskia\n')
186 f.write('include $(BUILD_SHARED_LIBRARY)\n')
187
188 def AddToList(li, item):
189 """
190 Add item to li, but only if it is not already in li.
191 """
192 if item not in li:
193 li.append(item)
194
195 def GetMembers(d, name):
196 """
197 Get the list corresponding to name in dictionary d.
198 """
199 if not name in d:
200 return []
201 return d[name]
202
203 def ParseDictionary(var_dict, d, current_target_name):
204 """
205 Helper function to get the meaningful entries in a dictionary.
206 @param var_dict VarsDict object for storing the results of the parsing.
207 @param d Dictionary object to parse.
208 @param current_target_name The current target being parsed. If this
209 dictionary is a target, this will be its entry
210 'target_name'. Otherwise, this will be the name of
211 the target which contains this dictionary.
212 """
213 for source in GetMembers(d, 'sources'):
214 if source.endswith('.h'):
215 # Android.mk does not need the header files.
216 continue
217 if source.endswith('gypi'):
218 # The gypi files are included in sources, but the sources they included
219 # are also included. No need to parse them again.
220 continue
221 # The path is relative to the gyp folder, but Android wants the path
222 # relative to the root.
223 source = source.replace('../src', 'src', 1)
224 AddToList(var_dict['sources'], source)
225
226 for lib in GetMembers(d, 'libraries'):
227 if lib.endswith('.a'):
228 # Remove the '.a'
229 lib = lib[:-2]
230 # Add 'lib', if necessary
231 if not lib.startswith('lib'):
232 lib = 'lib' + lib
233 AddToList(var_dict['static_libraries'], lib)
234 else:
235 # lib will be in the form of '-l<name>'. Change it to 'lib<name>'
236 lib = lib.replace('-l', 'lib', 1)
237 AddToList(var_dict['shared_libraries'], lib)
238
239 for dependency in GetMembers(d, 'dependencies'):
240 # Each dependency is listed as
241 # <path_to_file>:<target>#target
242 li = dependency.split(':')
243 assert(len(li) <= 2 and len(li) >= 1)
244 sub_targets = []
245 if len(li) == 2 and li[1] != '*':
246 sub_targets.append(li[1].split('#')[0])
247 sub_path = li[0]
248 assert(sub_path.endswith('.gyp'))
249 # Although the original reference is to a .gyp, parse the corresponding
250 # gypd file, which was constructed by gyp.
251 sub_path = sub_path + 'd'
252 ParseGypd(var_dict, sub_path, sub_targets)
253
254 if 'default_configuration' in d:
255 config_name = d['default_configuration']
256 # default_configuration is meaningless without configurations
257 assert('configurations' in d)
258 config = d['configurations'][config_name]
259 ParseDictionary(var_dict, config, current_target_name)
260
261 for flag in GetMembers(d, 'cflags'):
262 AddToList(var_dict['cflags'], flag)
263 for flag in GetMembers(d, 'cflags_cc'):
264 AddToList(var_dict['cppflags'], flag)
265
266 for include in GetMembers(d, 'include_dirs'):
267 # The input path will be relative to gyp/, but Android wants relative to
268 # LOCAL_PATH
269 include = include.replace('..', '$(LOCAL_PATH)', 1)
270 # Remove a trailing slash, if present.
271 if include.endswith('/'):
272 include = include[:-1]
273 AddToList(var_dict['include_dirs'], include)
274 # For the top level, libskia, include directories should be exported.
275 if current_target_name == 'libskia':
276 AddToList(var_dict['export_include_dirs'], include)
277
278 for define in GetMembers(d, 'defines'):
279 AddToList(var_dict['defines'], define)
280
281 def ParseGypd(var_dict, path, desired_targets=None):
282 """
283 Parse a gypd file.
284 @param var_dict VarsDict object for storing the result of the parse.
285 @param path Path to gypd file.
286 @param desired_targets List of targets to be parsed from this file. If empty,
287 parse all targets.
288 """
289 d = {}
290 with open(path, 'r') as f:
291 # Read the entire file as a dictionary
292 d = eval(f.read())
293 # The gypd file is structured such that the top level dictionary has an entry
294 # named 'targets'
295 if 'targets' not in d:
296 return
297 targets = d['targets']
298 for target in targets:
299 target_name = target['target_name']
300 if target_name in var_dict['known_targets']:
301 # Avoid circular dependencies
302 continue
303 if desired_targets and target_name not in desired_targets:
304 # Our caller does not depend on this one
305 continue
306 # Add it to our known targets so we don't parse it again
307 var_dict['known_targets'].append(target_name)
308
309 ParseDictionary(var_dict, target, target_name)
310
311 def Intersect(var_dict_list):
312 """
313 Find the intersection of a list of VarsDicts and trim each input to its
314 unique entries.
315 @param var_dict_list list of VarsDicts.
316 @return VarsDict containing list entries common to all VarsDicts in
317 var_dict_list
318 """
319 intersection = VarsDict()
320 # First VarsDict
321 var_dictA = var_dict_list[0]
322 # The rest.
323 otherVarDicts = var_dict_list[1:]
324
325 for key in var_dictA.keys():
326 # Copy A's list, so we can continue iterating after modifying the original.
327 aList = list(var_dictA[key])
328 for item in aList:
329 # If item is in all lists, add to intersection, and remove from all.
330 in_all_lists = True
331 for var_dict in otherVarDicts:
332 # Each dictionary must be a VarsDict, which always contains the same
333 # set of keys.
334 if not item in var_dict[key]:
335 in_all_lists = False
336 break
337 if in_all_lists:
338 intersection[key].append(item)
339 for var_dict in var_dict_list:
340 var_dict[key].remove(item)
341 return intersection
342
343 def CleanGypdFiles():
344 """
345 Remove the gypd files generated by android_framework_gyp.main().
346 """
347 assert os.path.isdir(GYP_FOLDER)
348 files = os.listdir(GYP_FOLDER)
349 for f in files:
350 if f.endswith('gypd'):
351 os.remove(os.path.join(GYP_FOLDER, f))
352
353 import android_framework_gyp
354
355 def main():
356 # Move up to top of trunk
357 script_dir = os.path.dirname(__file__)
358 skia_dir = os.path.normpath(os.path.join(script_dir, os.pardir, os.pardir,
359 os.pardir))
360 os.chdir(skia_dir)
361
362 main_gypd_file = os.path.join(GYP_FOLDER, 'android_framework_lib.gypd')
363
364 print 'Creating Android.mk',
365
366 # Call the script to generate gypd files, first using a non-existant archtype.
367 android_framework_gyp.main('other', False)
368 # And store its variables in the default dict
369 default_var_dict = VarsDict()
370 ParseGypd(default_var_dict, main_gypd_file)
371 CleanGypdFiles()
372
373 print '.',
374
375 # Now do the same for Arm. arm_var_dict will contain all the variable
376 # definitions needed to build with arm (meaning that it will share most
377 # with default_var_dict).
378 android_framework_gyp.main('arm', False)
379 arm_var_dict = VarsDict()
380 ParseGypd(arm_var_dict, main_gypd_file)
381 CleanGypdFiles()
382
383 print '.',
384
385 # Now do the same for Arm/Neon.
386 android_framework_gyp.main('arm', True)
387 arm_neon_var_dict = VarsDict()
388 ParseGypd(arm_neon_var_dict, main_gypd_file)
389 CleanGypdFiles()
390
391 print '.'
392
393 # Now do the same for x86.
394 x86_var_dict = VarsDict()
395 if INCLUDE_X86_OPTS:
396 android_framework_gyp.main('x86', False)
397 ParseGypd(x86_var_dict, main_gypd_file)
398 CleanGypdFiles()
399
400 # Compute the intersection of all targets. All the files in the intersection
401 # should be part of the makefile always. Each dict will now contain trimmed
402 # lists containing only variable definitions specific to that configuration.
403 var_dict_list = [default_var_dict, arm_var_dict, arm_neon_var_dict]
404 if INCLUDE_X86_OPTS:
405 var_dict_list.append(x86_var_dict)
406 common = Intersect(var_dict_list)
407
408 # Further trim arm_neon_var_dict with arm_var_dict. After this call,
409 # arm_var_dict (which will now be the intersection) includes all definitions
410 # used by both arm and arm + neon, and arm_neon_var_dict will only contain
411 # those specific to arm + neon.
412 arm_var_dict = Intersect([arm_var_dict, arm_neon_var_dict])
413
414 WriteAndroidMk(common, arm_var_dict, arm_neon_var_dict, x86_var_dict,
415 default_var_dict)
416
417 if __name__ == '__main__':
418 main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698