aboutsummaryrefslogtreecommitdiffhomepage
path: root/platform_tools/android/gyp_gen
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-02-06 16:13:00 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-02-06 16:13:00 +0000
commit89335631749b29ea92e55ed710f030692aa13297 (patch)
tree07ecc92f1ea39df152bda0c10d42c4b3563f96db /platform_tools/android/gyp_gen
parent1c188ed9fa70e50db1471bdbf683135c39ea85be (diff)
Scripts to generate Android.mk for framework Skia.
In order to create Android.mk, run >> python platform_tools/android/bin/gyp_to_android.py For the change in the Android.mk file, see https://googleplex-android-review.git.corp.google.com/#/c/408170/ (SkipBuildbotRuns) BUG=skia:1975 R=djsollen@google.com, epoger@google.com Author: scroggo@google.com Review URL: https://codereview.chromium.org/140503007 git-svn-id: http://skia.googlecode.com/svn/trunk@13344 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'platform_tools/android/gyp_gen')
-rw-r--r--platform_tools/android/gyp_gen/gypd_parser.py122
-rw-r--r--platform_tools/android/gyp_gen/makefile_writer.py175
-rw-r--r--platform_tools/android/gyp_gen/variables.py11
-rw-r--r--platform_tools/android/gyp_gen/vars_dict_lib.py133
4 files changed, 441 insertions, 0 deletions
diff --git a/platform_tools/android/gyp_gen/gypd_parser.py b/platform_tools/android/gyp_gen/gypd_parser.py
new file mode 100644
index 0000000000..b547c42525
--- /dev/null
+++ b/platform_tools/android/gyp_gen/gypd_parser.py
@@ -0,0 +1,122 @@
+#!/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.
+
+"""
+Functions for parsing the gypd output from gyp.
+"""
+
+import vars_dict_lib
+
+def parse_dictionary(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 d.get('sources', []):
+ # Compare against a lowercase version, in case files are named .H or .GYPI
+ lowercase_source = source.lower()
+ if lowercase_source.endswith('.h'):
+ # Android.mk does not need the header files.
+ continue
+ if lowercase_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)
+ var_dict.LOCAL_SRC_FILES.add(source)
+
+ for lib in d.get('libraries', []):
+ if lib.endswith('.a'):
+ # Remove the '.a'
+ lib = lib[:-2]
+ # Add 'lib', if necessary
+ if not lib.startswith('lib'):
+ lib = 'lib' + lib
+ var_dict.LOCAL_STATIC_LIBRARIES.add(lib)
+ else:
+ # lib will be in the form of '-l<name>'. Change it to 'lib<name>'
+ lib = lib.replace('-l', 'lib', 1)
+ var_dict.LOCAL_SHARED_LIBRARIES.add(lib)
+
+ for dependency in d.get('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'
+ parse_gypd(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]
+ parse_dictionary(var_dict, config, current_target_name)
+
+ for flag in d.get('cflags', []):
+ var_dict.LOCAL_CFLAGS.add(flag)
+ for flag in d.get('cflags_cc', []):
+ var_dict.LOCAL_CPPFLAGS.add(flag)
+
+ for include in d.get('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]
+ var_dict.LOCAL_C_INCLUDES.add(include)
+ # For the top level, libskia, include directories should be exported.
+ if current_target_name == 'libskia':
+ var_dict.LOCAL_EXPORT_C_INCLUDE_DIRS.add(include)
+
+ for define in d.get('defines', []):
+ var_dict.LOCAL_CFLAGS.add('-D' + define)
+
+
+def parse_gypd(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'
+ for target in d['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.add(target_name)
+
+ parse_dictionary(var_dict, target, target_name)
+
diff --git a/platform_tools/android/gyp_gen/makefile_writer.py b/platform_tools/android/gyp_gen/makefile_writer.py
new file mode 100644
index 0000000000..ea3a27ff1e
--- /dev/null
+++ b/platform_tools/android/gyp_gen/makefile_writer.py
@@ -0,0 +1,175 @@
+#!/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.
+
+"""
+Functions for creating an Android.mk from already created dictionaries.
+"""
+
+import os
+import variables
+
+def write_group(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 not items:
+ return
+
+ # Copy the list so we can prepend it with its name.
+ items_to_write = list(items)
+
+ if append:
+ items_to_write.insert(0, '%s +=' % name)
+ else:
+ items_to_write.insert(0, '%s :=' % name)
+
+ f.write(' \\\n\t'.join(items_to_write))
+
+ f.write('\n\n')
+
+
+def write_local_vars(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.
+ """
+ for key in var_dict.keys():
+ if key == 'LOCAL_CFLAGS':
+ # Always append LOCAL_CFLAGS. This allows us to define some early on in
+ # the makefile and not overwrite them.
+ _append = True
+ elif key == 'KNOWN_TARGETS':
+ # KNOWN_TARGETS are not needed in the final make file.
+ continue
+ else:
+ _append = append
+ write_group(f, key, var_dict[key], _append)
+
+
+AUTOGEN_WARNING = (
+"""
+###############################################################################
+#
+# THIS FILE IS AUTOGENERATED BY GYP_TO_ANDROID.PY. DO NOT EDIT.
+#
+###############################################################################
+
+"""
+)
+
+
+DEBUGGING_HELP = (
+"""
+###############################################################################
+#
+# 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.
+###############################################################################
+
+"""
+)
+
+
+# TODO (scroggo): Currently write_android_mk has intimate knowledge about its
+# parameters: e.g. arm_neon keeps track of differences from arm, whereas the
+# others keep track of differences from common. Consider reworking this.
+def write_android_mk(target_dir, common, arm, arm_neon, x86, default):
+ """
+ Given all the variables, write the final make file.
+ @param target_dir The full path to the directory to write Android.mk, or None
+ to use the current working directory.
+ @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 arm_neon 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.
+ """
+ target_file = 'Android.mk'
+ if target_dir:
+ target_file = os.path.join(target_dir, target_file)
+ with open(target_file, 'w') as f:
+ f.write(AUTOGEN_WARNING)
+ 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')
+
+ write_local_vars(f, common, False)
+
+ f.write('ifeq ($(TARGET_ARCH),arm)\n')
+ f.write('ifeq ($(ARCH_ARM_HAVE_NEON),true)\n')
+ write_local_vars(f, arm_neon, True)
+ f.write('endif\n\n')
+ write_local_vars(f, arm, True)
+
+ if variables.INCLUDE_X86_OPTS:
+ f.write('else ifeq ($(TARGET_ARCH),x86)\n')
+ write_local_vars(f, x86, True)
+
+ f.write('else\n')
+ write_local_vars(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')
+
+
+
diff --git a/platform_tools/android/gyp_gen/variables.py b/platform_tools/android/gyp_gen/variables.py
new file mode 100644
index 0000000000..326254a86e
--- /dev/null
+++ b/platform_tools/android/gyp_gen/variables.py
@@ -0,0 +1,11 @@
+#!/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.
+
+# TODO (scroggo): Currently the x86 specific files are not included. Include
+# them.
+INCLUDE_X86_OPTS = False
+
diff --git a/platform_tools/android/gyp_gen/vars_dict_lib.py b/platform_tools/android/gyp_gen/vars_dict_lib.py
new file mode 100644
index 0000000000..eedb8a36b9
--- /dev/null
+++ b/platform_tools/android/gyp_gen/vars_dict_lib.py
@@ -0,0 +1,133 @@
+#!/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.
+
+import collections
+import types
+
+class OrderedSet(object):
+ """
+ Ordered set of unique items that supports addition and removal.
+ """
+
+ def __init__(self):
+ self.__li = []
+
+ def add(self, item):
+ """
+ Add item, if it is not already in the set.
+ @param item The item to add.
+ """
+ if item not in self.__li:
+ self.__li.append(item)
+
+ def __contains__(self, item):
+ """
+ Whether the set contains item.
+ @param item The item to search for in the set.
+ @return bool Whether the item is in the set.
+ """
+ return item in self.__li
+
+ def __iter__(self):
+ """
+ Iterator for the set.
+ """
+ return self.__li.__iter__()
+
+ def remove(self, item):
+ """
+ Remove item from the set.
+ @param item Item to be removed.
+ """
+ return self.__li.remove(item)
+
+ def __len__(self):
+ """
+ Number of items in the set.
+ """
+ return len(self.__li)
+
+ def __getitem__(self, index):
+ """
+ Return item at index.
+ """
+ return self.__li[index]
+
+VAR_NAMES = ['LOCAL_CFLAGS',
+ 'LOCAL_CPPFLAGS',
+ 'LOCAL_SRC_FILES',
+ 'LOCAL_SHARED_LIBRARIES',
+ 'LOCAL_STATIC_LIBRARIES',
+ 'LOCAL_C_INCLUDES',
+ 'LOCAL_EXPORT_C_INCLUDE_DIRS',
+ 'KNOWN_TARGETS']
+
+class VarsDict(collections.namedtuple('VarsDict', VAR_NAMES)):
+ """
+ Custom class for storing the arguments to Android.mk variables. Can be
+ treated as a dictionary with fixed keys.
+ """
+
+ __slots__ = ()
+
+ def __new__(cls):
+ lists = []
+ # TODO (scroggo): Is there a better way add N items?
+ for __unused__ in range(len(VAR_NAMES)):
+ lists.append(OrderedSet())
+ return tuple.__new__(cls, lists)
+
+ def keys(self):
+ """
+ Return the field names as strings.
+ """
+ return self._fields
+
+ def __getitem__(self, index):
+ """
+ Return an item, indexed by a number or a string.
+ """
+ if type(index) == types.IntType:
+ # Treat the index as an array index into a tuple.
+ return tuple.__getitem__(self, index)
+ if type(index) == types.StringType:
+ # Treat the index as a key into a dictionary.
+ return eval('self.%s' % index)
+ return None
+
+
+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. WARNING: each VarsDict will be
+ modified in place, to remove the common elements!
+ @return VarsDict containing list entries common to all VarsDicts in
+ var_dict_list
+ """
+ intersection = VarsDict()
+ # First VarsDict
+ var_dict_a = var_dict_list[0]
+ # The rest.
+ other_var_dicts = var_dict_list[1:]
+
+ for key in var_dict_a.keys():
+ # Copy A's list, so we can continue iterating after modifying the original.
+ a_list = list(var_dict_a[key])
+ for item in a_list:
+ # If item is in all lists, add to intersection, and remove from all.
+ in_all_lists = True
+ for var_dict in other_var_dicts:
+ if not item in var_dict[key]:
+ in_all_lists = False
+ break
+ if in_all_lists:
+ intersection[key].add(item)
+ for var_dict in var_dict_list:
+ var_dict[key].remove(item)
+ return intersection
+