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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: platform_tools/android/bin/gyp_to_android.py
diff --git a/platform_tools/android/bin/gyp_to_android.py b/platform_tools/android/bin/gyp_to_android.py
new file mode 100644
index 0000000000000000000000000000000000000000..9d887c4c2d5320a0c675f7eb1a4c189d33ee5214
--- /dev/null
+++ b/platform_tools/android/bin/gyp_to_android.py
@@ -0,0 +1,418 @@
+#!/usr/bin/python
+
+# Copyright 2014 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+ Script for generating the Android framework's verision of Skia from gyp
+ files.
+"""
+
+import os
+
+# Folder containing all gyp files and generated gypd files.
+GYP_FOLDER = 'gyp'
+
+# FIXME: Currently the x86 specific files are not included. Include them.
+INCLUDE_X86_OPTS = False
+
+def WriteGroup(f, name, items, append):
+ """
+ Helper function to list all names passed to a variable.
+ @param f File open for writing (Android.mk)
+ @param name Name of the makefile variable (e.g. LOCAL_CFLAGS)
+ @param items list of strings to be passed to the variable.
+ @param append Whether to append to the variable or overwrite it.
+ """
+ if len(items) == 0:
+ return
+ if append:
+ f.write('%s += \\\n' % name)
+ else:
+ f.write('%s := \\\n' % name)
+ for item in items:
+ f.write('\t%s \\\n' % item)
+ # Add an extra blank line since the last line ended in a \
+ f.write('\n')
+
+class VarsDict(dict):
+ """
+ Custom dict of lists for each makefile variable. Does not allow insertion of
+ new keys.
+ """
+ def __init__(self):
+ dict.__init__(self)
+ self.__allow_insert = True
+ self['cflags'] = []
+ self['cppflags'] = []
+ self['defines'] = []
+ self['sources'] = []
+ self['shared_libraries'] = []
+ self['static_libraries'] = []
+ self['include_dirs'] = []
+ self['export_include_dirs'] = []
+ self['known_targets'] = []
+ self.__allow_insert = False
+ self.__allow_convert = True
+
+ def __setitem__(self, k, v):
+ # Prevent the insertion of arbitrary keys.
+ assert self.__allow_insert
+ dict.__setitem__(self, k, v)
+
+ def ConvertDefines(self):
+ """
+ Add each string in 'defines' to 'cflags', prepended with '-D'. Can only
+ be done once.
+ """
+ assert self.__allow_convert
+ self.__allow_convert = False
+ for define in self['defines']:
+ define = '-D' + define
+ self['cflags'].append(define)
+
+def WriteLocalVars(f, var_dict, append):
+ """
+ Helper function to write all the members of var_dict to the makefile.
+ @param f File open for writing (Android.mk)
+ @param var_dict VarsDict holding the unique values for one configuration.
+ @param append Whether to append to each makefile variable or overwrite it.
+ """
+ var_dict.ConvertDefines()
+ # Always append LOCAL_CFLAGS. This allows us to define some early on in the
+ # makefile and not overwrite them.
+ WriteGroup(f, 'LOCAL_CFLAGS', var_dict['cflags'], True)
+ WriteGroup(f, 'LOCAL_CPPFLAGS', var_dict['cppflags'], append)
+ WriteGroup(f, 'LOCAL_SRC_FILES', var_dict['sources'], append)
+ WriteGroup(f, 'LOCAL_SHARED_LIBRARIES', var_dict['shared_libraries'], append)
+ WriteGroup(f, 'LOCAL_STATIC_LIBRARIES', var_dict['static_libraries'], append)
+ WriteGroup(f, 'LOCAL_C_INCLUDES', var_dict['include_dirs'], append)
+ WriteGroup(f, 'LOCAL_EXPORT_C_INCLUDE_DIRS', var_dict['export_include_dirs'],
+ append)
+
+DEBUGGING_HELP = (
+"""
+###############################################################################
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.
+#
+# PROBLEMS WITH SKIA DEBUGGING?? READ THIS...
+#
+# The debug build results in changes to the Skia headers. This means that those
+# using libskia must also be built with the debug version of the Skia headers.
+# There are a few scenarios where this comes into play:
+#
+# (1) You're building debug code that depends on libskia.
+# (a) If libskia is built in release, then define SK_RELEASE when building
+# your sources.
+# (b) If libskia is built with debugging (see step 2), then no changes are
+# needed since your sources and libskia have been built with SK_DEBUG.
+# (2) You're building libskia in debug mode.
+# (a) RECOMMENDED: You can build the entire system in debug mode. Do this by
+# updating your build/config.mk to include -DSK_DEBUG on the line that
+# defines COMMON_GLOBAL_CFLAGS
+# (b) You can update all the users of libskia to define SK_DEBUG when they are
+# building their sources.
+#
+# NOTE: If neither SK_DEBUG or SK_RELEASE are defined then Skia checks NDEBUG to
+# determine which build type to use.
+###############################################################################
+
+"""
+)
+
+def WriteAndroidMk(common, arm, armNeon, x86, default):
+ """
+ Given all the variables, write the final make file.
+ @param common VarsDict holding variables definitions common to all
+ configurations.
+ @param arm VarsDict holding variable definitions unique to arm. Will be
+ written to the makefile inside an 'ifeq ($(TARGET_ARCH), arm)'
+ block.
+ @param armNeon VarsDict holding variable definitions unique to arm with neon.
+ Will be written inside an 'ifeq ($(ARCH_ARM_HAVE_NEON),true)'
+ block nested inside an 'ifeq ($(TARGET_ARCH), arm)' block.
+ @param x86 VarsDict holding variable definitions unique to x86. Will be
+ written inside an 'ifeq ($(TARGET_ARCH),x86)' block.
+ @param default VarsDict holding variable definitions for an architecture
+ without custom optimizations.
+ TODO: Add mips.
+ """
+ with open('Android.mk', 'w') as f:
+ f.write('BASE_PATH := $(call my-dir)\n')
+ f.write('LOCAL_PATH:= $(call my-dir)\n')
+
+ f.write(DEBUGGING_HELP)
+
+ f.write('include $(CLEAR_VARS)\n')
+
+ f.write('LOCAL_ARM_MODE := thumb\n')
+
+ # need a flag to tell the C side when we're on devices with large memory
+ # budgets (i.e. larger than the low-end devices that initially shipped)
+ f.write('ifeq ($(ARCH_ARM_HAVE_VFP),true)\n')
+ f.write('\tLOCAL_CFLAGS += -DANDROID_LARGE_MEMORY_DEVICE\n')
+ f.write('endif\n\n')
+
+ f.write('ifeq ($(TARGET_ARCH),x86)\n')
+ f.write('\tLOCAL_CFLAGS += -DANDROID_LARGE_MEMORY_DEVICE\n')
+ f.write('endif\n\n')
+
+ f.write('# used for testing\n')
+ f.write('#LOCAL_CFLAGS += -g -O0\n\n')
+
+ f.write('ifeq ($(NO_FALLBACK_FONT),true)\n')
+ f.write('\tLOCAL_CFLAGS += -DNO_FALLBACK_FONT\n')
+ f.write('endif\n\n')
+
+ WriteLocalVars(f, common, False)
+
+ f.write('ifeq ($(TARGET_ARCH),arm)\n')
+ f.write('ifeq ($(ARCH_ARM_HAVE_NEON),true)\n')
+ WriteLocalVars(f, armNeon, True)
+ f.write('endif\n\n')
+ WriteLocalVars(f, arm, True)
+
+ if INCLUDE_X86_OPTS:
+ f.write('else ifeq ($(TARGET_ARCH),x86)\n')
+ WriteLocalVars(f, x86, True)
+
+ f.write('else\n')
+ WriteLocalVars(f, default, True)
+ f.write('endif\n\n')
+
+ f.write('include external/stlport/libstlport.mk\n')
+ f.write('LOCAL_MODULE:= libskia\n')
+ f.write('include $(BUILD_SHARED_LIBRARY)\n')
+
+def AddToList(li, item):
+ """
+ Add item to li, but only if it is not already in li.
+ """
+ if item not in li:
+ li.append(item)
+
+def GetMembers(d, name):
+ """
+ Get the list corresponding to name in dictionary d.
+ """
+ if not name in d:
+ return []
+ return d[name]
+
+def ParseDictionary(var_dict, d, current_target_name):
+ """
+ Helper function to get the meaningful entries in a dictionary.
+ @param var_dict VarsDict object for storing the results of the parsing.
+ @param d Dictionary object to parse.
+ @param current_target_name The current target being parsed. If this
+ dictionary is a target, this will be its entry
+ 'target_name'. Otherwise, this will be the name of
+ the target which contains this dictionary.
+ """
+ for source in GetMembers(d, 'sources'):
+ if source.endswith('.h'):
+ # Android.mk does not need the header files.
+ continue
+ if source.endswith('gypi'):
+ # The gypi files are included in sources, but the sources they included
+ # are also included. No need to parse them again.
+ continue
+ # The path is relative to the gyp folder, but Android wants the path
+ # relative to the root.
+ source = source.replace('../src', 'src', 1)
+ AddToList(var_dict['sources'], source)
+
+ for lib in GetMembers(d, 'libraries'):
+ if lib.endswith('.a'):
+ # Remove the '.a'
+ lib = lib[:-2]
+ # Add 'lib', if necessary
+ if not lib.startswith('lib'):
+ lib = 'lib' + lib
+ AddToList(var_dict['static_libraries'], lib)
+ else:
+ # lib will be in the form of '-l<name>'. Change it to 'lib<name>'
+ lib = lib.replace('-l', 'lib', 1)
+ AddToList(var_dict['shared_libraries'], lib)
+
+ for dependency in GetMembers(d, 'dependencies'):
+ # Each dependency is listed as
+ # <path_to_file>:<target>#target
+ li = dependency.split(':')
+ assert(len(li) <= 2 and len(li) >= 1)
+ sub_targets = []
+ if len(li) == 2 and li[1] != '*':
+ sub_targets.append(li[1].split('#')[0])
+ sub_path = li[0]
+ assert(sub_path.endswith('.gyp'))
+ # Although the original reference is to a .gyp, parse the corresponding
+ # gypd file, which was constructed by gyp.
+ sub_path = sub_path + 'd'
+ ParseGypd(var_dict, sub_path, sub_targets)
+
+ if 'default_configuration' in d:
+ config_name = d['default_configuration']
+ # default_configuration is meaningless without configurations
+ assert('configurations' in d)
+ config = d['configurations'][config_name]
+ ParseDictionary(var_dict, config, current_target_name)
+
+ for flag in GetMembers(d, 'cflags'):
+ AddToList(var_dict['cflags'], flag)
+ for flag in GetMembers(d, 'cflags_cc'):
+ AddToList(var_dict['cppflags'], flag)
+
+ for include in GetMembers(d, 'include_dirs'):
+ # The input path will be relative to gyp/, but Android wants relative to
+ # LOCAL_PATH
+ include = include.replace('..', '$(LOCAL_PATH)', 1)
+ # Remove a trailing slash, if present.
+ if include.endswith('/'):
+ include = include[:-1]
+ AddToList(var_dict['include_dirs'], include)
+ # For the top level, libskia, include directories should be exported.
+ if current_target_name == 'libskia':
+ AddToList(var_dict['export_include_dirs'], include)
+
+ for define in GetMembers(d, 'defines'):
+ AddToList(var_dict['defines'], define)
+
+def ParseGypd(var_dict, path, desired_targets=None):
+ """
+ Parse a gypd file.
+ @param var_dict VarsDict object for storing the result of the parse.
+ @param path Path to gypd file.
+ @param desired_targets List of targets to be parsed from this file. If empty,
+ parse all targets.
+ """
+ d = {}
+ with open(path, 'r') as f:
+ # Read the entire file as a dictionary
+ d = eval(f.read())
+ # The gypd file is structured such that the top level dictionary has an entry
+ # named 'targets'
+ if 'targets' not in d:
+ return
+ targets = d['targets']
+ for target in targets:
+ target_name = target['target_name']
+ if target_name in var_dict['known_targets']:
+ # Avoid circular dependencies
+ continue
+ if desired_targets and target_name not in desired_targets:
+ # Our caller does not depend on this one
+ continue
+ # Add it to our known targets so we don't parse it again
+ var_dict['known_targets'].append(target_name)
+
+ ParseDictionary(var_dict, target, target_name)
+
+def Intersect(var_dict_list):
+ """
+ Find the intersection of a list of VarsDicts and trim each input to its
+ unique entries.
+ @param var_dict_list list of VarsDicts.
+ @return VarsDict containing list entries common to all VarsDicts in
+ var_dict_list
+ """
+ intersection = VarsDict()
+ # First VarsDict
+ var_dictA = var_dict_list[0]
+ # The rest.
+ otherVarDicts = var_dict_list[1:]
+
+ for key in var_dictA.keys():
+ # Copy A's list, so we can continue iterating after modifying the original.
+ aList = list(var_dictA[key])
+ for item in aList:
+ # If item is in all lists, add to intersection, and remove from all.
+ in_all_lists = True
+ for var_dict in otherVarDicts:
+ # Each dictionary must be a VarsDict, which always contains the same
+ # set of keys.
+ if not item in var_dict[key]:
+ in_all_lists = False
+ break
+ if in_all_lists:
+ intersection[key].append(item)
+ for var_dict in var_dict_list:
+ var_dict[key].remove(item)
+ return intersection
+
+def CleanGypdFiles():
+ """
+ Remove the gypd files generated by android_framework_gyp.main().
+ """
+ assert os.path.isdir(GYP_FOLDER)
+ files = os.listdir(GYP_FOLDER)
+ for f in files:
+ if f.endswith('gypd'):
+ os.remove(os.path.join(GYP_FOLDER, f))
+
+import android_framework_gyp
+
+def main():
+ # Move up to top of trunk
+ script_dir = os.path.dirname(__file__)
+ skia_dir = os.path.normpath(os.path.join(script_dir, os.pardir, os.pardir,
+ os.pardir))
+ os.chdir(skia_dir)
+
+ main_gypd_file = os.path.join(GYP_FOLDER, 'android_framework_lib.gypd')
+
+ print 'Creating Android.mk',
+
+ # Call the script to generate gypd files, first using a non-existant archtype.
+ android_framework_gyp.main('other', False)
+ # And store its variables in the default dict
+ default_var_dict = VarsDict()
+ ParseGypd(default_var_dict, main_gypd_file)
+ CleanGypdFiles()
+
+ print '.',
+
+ # Now do the same for Arm. arm_var_dict will contain all the variable
+ # definitions needed to build with arm (meaning that it will share most
+ # with default_var_dict).
+ android_framework_gyp.main('arm', False)
+ arm_var_dict = VarsDict()
+ ParseGypd(arm_var_dict, main_gypd_file)
+ CleanGypdFiles()
+
+ print '.',
+
+ # Now do the same for Arm/Neon.
+ android_framework_gyp.main('arm', True)
+ arm_neon_var_dict = VarsDict()
+ ParseGypd(arm_neon_var_dict, main_gypd_file)
+ CleanGypdFiles()
+
+ print '.'
+
+ # Now do the same for x86.
+ x86_var_dict = VarsDict()
+ if INCLUDE_X86_OPTS:
+ android_framework_gyp.main('x86', False)
+ ParseGypd(x86_var_dict, main_gypd_file)
+ CleanGypdFiles()
+
+ # Compute the intersection of all targets. All the files in the intersection
+ # should be part of the makefile always. Each dict will now contain trimmed
+ # lists containing only variable definitions specific to that configuration.
+ var_dict_list = [default_var_dict, arm_var_dict, arm_neon_var_dict]
+ if INCLUDE_X86_OPTS:
+ var_dict_list.append(x86_var_dict)
+ common = Intersect(var_dict_list)
+
+ # Further trim arm_neon_var_dict with arm_var_dict. After this call,
+ # arm_var_dict (which will now be the intersection) includes all definitions
+ # used by both arm and arm + neon, and arm_neon_var_dict will only contain
+ # those specific to arm + neon.
+ arm_var_dict = Intersect([arm_var_dict, arm_neon_var_dict])
+
+ WriteAndroidMk(common, arm_var_dict, arm_neon_var_dict, x86_var_dict,
+ default_var_dict)
+
+if __name__ == '__main__':
+ main()

Powered by Google App Engine
This is Rietveld 408576698