OLD | NEW |
---|---|
(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() | |
OLD | NEW |