1#!/usr/bin/env python 2# 3# Copyright 2016 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# Generate Android.bp for Skia from GN configuration. 9 10import json 11import os 12import pprint 13import string 14import subprocess 15import tempfile 16 17tool_cflags = [ 18 '-Wno-unused-parameter', 19] 20 21# It's easier to maintain one list instead of separate lists. 22tool_shared_libs = [ 23 'liblog', 24 'libGLESv2', 25 'libEGL', 26 'libvulkan', 27 'libz', 28 'libjpeg', 29 'libpng', 30 'libicuuc', 31 'libicui18n', 32 'libexpat', 33 'libft2', 34 'libheif', 35 'libdng_sdk', 36 'libpiex', 37 'libcutils', 38 'libnativewindow', 39] 40 41# The ordering here is important: libsfntly needs to come after libskia. 42tool_static_libs = [ 43 'libarect', 44 'libjsoncpp', 45 'libskia', 46 'libsfntly', 47 'libwebp-decode', 48 'libwebp-encode', 49] 50 51# First we start off with a template for Android.bp, 52# with holes for source lists and include directories. 53bp = string.Template('''// This file is autogenerated by gn_to_bp.py. 54 55cc_library { 56 name: "libskia", 57 cflags: [ 58 "-fexceptions", 59 "-Wno-unused-parameter", 60 "-U_FORTIFY_SOURCE", 61 "-D_FORTIFY_SOURCE=1", 62 "-DSKIA_IMPLEMENTATION=1", 63 "-DATRACE_TAG=ATRACE_TAG_VIEW", 64 ], 65 66 export_include_dirs: [ 67 $export_includes 68 ], 69 70 local_include_dirs: [ 71 $local_includes 72 ], 73 74 srcs: [ 75 $srcs 76 ], 77 78 arch: { 79 arm: { 80 srcs: [ 81 $arm_srcs 82 ], 83 84 armv7_a_neon: { 85 srcs: [ 86 $arm_neon_srcs 87 ], 88 }, 89 }, 90 91 arm64: { 92 srcs: [ 93 $arm64_srcs 94 ], 95 }, 96 97 mips: { 98 srcs: [ 99 $none_srcs 100 ], 101 }, 102 103 mips64: { 104 srcs: [ 105 $none_srcs 106 ], 107 }, 108 109 x86: { 110 srcs: [ 111 $x86_srcs 112 ], 113 }, 114 115 x86_64: { 116 srcs: [ 117 $x86_srcs 118 ], 119 }, 120 }, 121 122 shared_libs: [ 123 "libEGL", 124 "libGLESv2", 125 "libdng_sdk", 126 "libexpat", 127 "libft2", 128 "libheif", 129 "libicui18n", 130 "libicuuc", 131 "libjpeg", 132 "liblog", 133 "libpiex", 134 "libpng", 135 "libvulkan", 136 "libz", 137 "libcutils", 138 "libnativewindow", 139 ], 140 static_libs: [ 141 "libarect", 142 "libsfntly", 143 "libwebp-decode", 144 "libwebp-encode", 145 ], 146} 147 148cc_test { 149 name: "skia_dm", 150 151 cflags: [ 152 $tool_cflags 153 ], 154 155 local_include_dirs: [ 156 $dm_includes 157 ], 158 159 srcs: [ 160 $dm_srcs 161 ], 162 163 shared_libs: [ 164 $tool_shared_libs 165 ], 166 167 static_libs: [ 168 $tool_static_libs 169 ], 170} 171 172cc_test { 173 name: "skia_nanobench", 174 175 cflags: [ 176 $tool_cflags 177 ], 178 179 local_include_dirs: [ 180 $nanobench_includes 181 ], 182 183 srcs: [ 184 $nanobench_srcs 185 ], 186 187 shared_libs: [ 188 $tool_shared_libs 189 ], 190 191 static_libs: [ 192 $tool_static_libs 193 ], 194}''') 195 196# We'll run GN to get the main source lists and include directories for Skia. 197gn_args = { 198 'is_official_build': 'true', 199 'skia_enable_tools': 'true', 200 'skia_use_libheif': 'true', 201 'skia_use_vulkan': 'true', 202 'target_cpu': '"none"', 203 'target_os': '"android"', 204} 205gn_args = ' '.join(sorted('%s=%s' % (k,v) for (k,v) in gn_args.iteritems())) 206 207tmp = tempfile.mkdtemp() 208subprocess.check_call(['gn', 'gen', tmp, '--args=%s' % gn_args, '--ide=json']) 209 210js = json.load(open(os.path.join(tmp, 'project.json'))) 211 212def strip_slashes(lst): 213 return {str(p.lstrip('/')) for p in lst} 214 215srcs = strip_slashes(js['targets']['//:skia']['sources']) 216local_includes = strip_slashes(js['targets']['//:skia']['include_dirs']) 217export_includes = strip_slashes(js['targets']['//:public']['include_dirs']) 218 219dm_srcs = strip_slashes(js['targets']['//:dm']['sources']) 220dm_includes = strip_slashes(js['targets']['//:dm']['include_dirs']) 221 222nanobench_target = js['targets']['//:nanobench'] 223nanobench_srcs = strip_slashes(nanobench_target['sources']) 224nanobench_includes = strip_slashes(nanobench_target['include_dirs']) 225 226def GrabDependentSrcs(name, srcs_to_extend, exclude): 227 # Grab the sources from other targets that $name depends on (e.g. optional 228 # Skia components, gms, tests, etc). 229 for dep in js['targets'][name]['deps']: 230 if 'third_party' in dep: 231 continue # We've handled all third-party DEPS as static or shared_libs. 232 if 'none' in dep: 233 continue # We'll handle all cpu-specific sources manually later. 234 if exclude and exclude in dep: 235 continue 236 srcs_to_extend.update(strip_slashes(js['targets'][dep].get('sources', []))) 237 GrabDependentSrcs(dep, srcs_to_extend, exclude) 238 239GrabDependentSrcs('//:skia', srcs, None) 240GrabDependentSrcs('//:dm', dm_srcs, 'skia') 241GrabDependentSrcs('//:nanobench', nanobench_srcs, 'skia') 242 243# No need to list headers. 244srcs = {s for s in srcs if not s.endswith('.h')} 245dm_srcs = {s for s in dm_srcs if not s.endswith('.h')} 246nanobench_srcs = {s for s in nanobench_srcs if not s.endswith('.h')} 247 248# Most defines go into SkUserConfig.h, where they're seen by Skia and its users. 249defines = [str(d) for d in js['targets']['//:skia']['defines']] 250defines.remove('NDEBUG') # Let the Android build control this. 251defines.remove('SKIA_IMPLEMENTATION=1') # Only libskia should have this define. 252 253# For architecture specific files, it's easier to just read the same source 254# that GN does (opts.gni) rather than re-run GN once for each architecture. 255 256# This .gni file we want to read is close enough to Python syntax 257# that we can use execfile() if we supply definitions for GN builtins. 258 259def get_path_info(path, kind): 260 assert kind == "abspath" 261 # While we want absolute paths in GN, relative paths work best here. 262 return path 263 264builtins = { 'get_path_info': get_path_info } 265defs = {} 266here = os.path.dirname(__file__) 267execfile(os.path.join(here, 'opts.gni'), builtins, defs) 268 269# Turn paths from opts.gni into paths relative to external/skia. 270def scrub(lst): 271 # Perform any string substitutions. 272 for var in defs: 273 if type(defs[var]) is str: 274 lst = [ p.replace('$'+var, defs[var]) for p in lst ] 275 # Relativize paths to top-level skia/ directory. 276 return [os.path.relpath(p, '..') for p in lst] 277 278# Turn a list of strings into the style bpfmt outputs. 279def bpfmt(indent, lst, sort=True): 280 if sort: 281 lst = sorted(lst) 282 return ('\n' + ' '*indent).join('"%s",' % v for v in lst) 283 284# OK! We have everything to fill in Android.bp... 285with open('Android.bp', 'w') as f: 286 print >>f, bp.substitute({ 287 'export_includes': bpfmt(8, export_includes), 288 'local_includes': bpfmt(8, local_includes), 289 'srcs': bpfmt(8, srcs), 290 291 'arm_srcs': bpfmt(16, scrub(defs['armv7'])), 292 'arm_neon_srcs': bpfmt(20, scrub(defs['neon'])), 293 'arm64_srcs': bpfmt(16, scrub(defs['arm64'] + 294 defs['crc32'])), 295 'none_srcs': bpfmt(16, scrub(defs['none'])), 296 'x86_srcs': bpfmt(16, scrub(defs['sse2'] + 297 defs['ssse3'] + 298 defs['sse41'] + 299 defs['sse42'] + 300 defs['avx' ] + 301 defs['hsw' ])), 302 303 'tool_cflags' : bpfmt(8, tool_cflags), 304 'tool_shared_libs' : bpfmt(8, tool_shared_libs), 305 'tool_static_libs' : bpfmt(8, tool_static_libs, False), 306 307 'dm_includes' : bpfmt(8, dm_includes), 308 'dm_srcs' : bpfmt(8, dm_srcs), 309 310 'nanobench_includes' : bpfmt(8, nanobench_includes), 311 'nanobench_srcs' : bpfmt(8, nanobench_srcs), 312 }) 313 314#... and all the #defines we want to put in SkUserConfig.h. 315with open('include/config/SkUserConfig.h', 'w') as f: 316 print >>f, '// DO NOT MODIFY! This file is autogenerated by gn_to_bp.py.' 317 print >>f, '// If need to change a define, modify SkUserConfigManual.h' 318 print >>f, '#ifndef SkUserConfig_DEFINED' 319 print >>f, '#define SkUserConfig_DEFINED' 320 print >>f, '#include "SkUserConfigManual.h"' 321 for define in sorted(defines): 322 print >>f, ' #define', define.replace('=', ' ') 323 print >>f, '#endif//SkUserConfig_DEFINED' 324