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