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