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

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

Powered by Google App Engine
This is Rietveld 408576698