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 os
11import pprint
12import string
13import subprocess
14import tempfile
15
16import gn_to_bp_utils
17
18# First we start off with a template for Android.bp,
19# with holes for source lists and include directories.
20bp = string.Template('''// This file is autogenerated by gn_to_bp.py.
21
22cc_library_static {
23    name: "libskia",
24    host_supported: true,
25    cflags: [
26        $cflags
27    ],
28
29    cppflags:[
30        $cflags_cc
31    ],
32
33    export_include_dirs: [
34        $export_includes
35    ],
36
37    local_include_dirs: [
38        $local_includes
39    ],
40
41    srcs: [
42        $srcs
43    ],
44
45    arch: {
46        arm: {
47            srcs: [
48                $arm_srcs
49            ],
50
51            neon: {
52                srcs: [
53                    $arm_neon_srcs
54                ],
55            },
56        },
57
58        arm64: {
59            srcs: [
60                $arm64_srcs
61            ],
62        },
63
64        mips: {
65            srcs: [
66                $none_srcs
67            ],
68        },
69
70        mips64: {
71            srcs: [
72                $none_srcs
73            ],
74        },
75
76        x86: {
77            srcs: [
78                $x86_srcs
79            ],
80        },
81
82        x86_64: {
83            srcs: [
84                $x86_srcs
85            ],
86        },
87    },
88
89    target: {
90      android: {
91        srcs: [
92          $android_srcs
93          "third_party/vulkanmemoryallocator/GrVulkanMemoryAllocator.cpp",
94        ],
95        local_include_dirs: [
96          "include/config/android",
97          "third_party/vulkanmemoryallocator/",
98        ],
99        export_include_dirs: [
100          "include/config/android",
101        ],
102      },
103      linux_glibc: {
104        cflags: [
105          "-mssse3",
106        ],
107        srcs: [
108          $linux_srcs
109        ],
110        local_include_dirs: [
111          "include/config/linux",
112        ],
113        export_include_dirs: [
114          "include/config/linux",
115        ],
116      },
117      darwin: {
118        cflags: [
119          "-mssse3",
120        ],
121        srcs: [
122          $mac_srcs
123        ],
124        local_include_dirs: [
125          "include/config/mac",
126        ],
127        export_include_dirs: [
128          "include/config/mac",
129        ],
130      },
131    },
132
133    defaults: ["skia_deps",
134               "skia_pgo",
135    ],
136}
137
138// Build libskia with PGO by default.
139// Location of PGO profile data is defined in build/soong/cc/pgo.go
140// and is separate from skia.
141// To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable
142// or set enable_profile_use property to false.
143cc_defaults {
144    name: "skia_pgo",
145    pgo: {
146        instrumentation: true,
147        profile_file: "hwui/hwui.profdata",
148        benchmarks: ["hwui", "skia"],
149        enable_profile_use: true,
150    },
151}
152
153// "defaults" property to disable profile use for Skia tools and benchmarks.
154cc_defaults {
155    name: "skia_pgo_no_profile_use",
156    defaults: [
157        "skia_pgo",
158    ],
159    pgo: {
160        enable_profile_use: false,
161    },
162}
163
164cc_defaults {
165    name: "skia_deps",
166    shared_libs: [
167        "libandroidicu",
168        "libdng_sdk",
169        "libexpat",
170        "libft2",
171        "libjpeg",
172        "liblog",
173        "libpiex",
174        "libpng",
175        "libz",
176        "libcutils",
177    ],
178    static_libs: [
179        "libarect",
180        "libsfntly",
181        "libwebp-decode",
182        "libwebp-encode",
183    ],
184    group_static_libs: true,
185    target: {
186      android: {
187        shared_libs: [
188            "libEGL",
189            "libGLESv2",
190            "libheif",
191            "libvulkan",
192            "libnativewindow",
193        ],
194      },
195      darwin: {
196        host_ldlibs: [
197            "-framework AppKit",
198        ],
199      },
200    },
201}
202
203cc_defaults {
204    name: "skia_tool_deps",
205    defaults: [
206        "skia_deps",
207        "skia_pgo_no_profile_use"
208    ],
209    static_libs: [
210        "libjsoncpp",
211        "libskia",
212    ],
213    cflags: [
214        "-Wno-implicit-fallthrough",
215        "-Wno-unused-parameter",
216        "-Wno-unused-variable",
217    ],
218}
219
220cc_test {
221    name: "skia_dm",
222
223    defaults: [
224        "skia_tool_deps"
225    ],
226
227    local_include_dirs: [
228        $dm_includes
229    ],
230
231    srcs: [
232        $dm_srcs
233    ],
234
235    shared_libs: [
236        "libbinder",
237        "libutils",
238    ],
239}
240
241cc_test {
242    name: "skia_nanobench",
243
244    defaults: [
245        "skia_tool_deps"
246    ],
247
248    local_include_dirs: [
249        $nanobench_includes
250    ],
251
252    srcs: [
253        $nanobench_srcs
254    ],
255
256    data: [
257        "resources/*",
258    ],
259}''')
260
261# We'll run GN to get the main source lists and include directories for Skia.
262def generate_args(target_os, enable_gpu):
263  d = {
264    'is_official_build':                  'true',
265
266    # gn_to_bp_utils' GetArchSources will take care of architecture-specific
267    # files.
268    'target_cpu':                         '"none"',
269
270    # Use the custom FontMgr, as the framework will handle fonts.
271    'skia_enable_fontmgr_custom':         'false',
272    'skia_enable_fontmgr_custom_empty':   'true',
273    'skia_enable_fontmgr_android':        'false',
274    'skia_use_fonthost_mac':              'false',
275
276    'skia_use_freetype':                  'true',
277    'skia_use_fontconfig':                'false',
278    'skia_use_fixed_gamma_text':          'true',
279  }
280  d['target_os'] = target_os
281  if target_os == '"android"':
282    d['skia_enable_tools'] = 'true'
283    d['skia_use_libheif']  = 'true'
284  else:
285    d['skia_use_libheif']  = 'false'
286
287  if enable_gpu:
288    d['skia_use_vulkan']   = 'true'
289  else:
290    d['skia_use_vulkan']   = 'false'
291    d['skia_enable_gpu']   = 'false'
292  return d
293
294gn_args       = generate_args('"android"', True)
295gn_args_linux = generate_args('"linux"',   False)
296gn_args_mac   = generate_args('"mac"',     False)
297
298js = gn_to_bp_utils.GenerateJSONFromGN(gn_args)
299
300def strip_slashes(lst):
301  return {str(p.lstrip('/')) for p in lst}
302
303android_srcs    = strip_slashes(js['targets']['//:skia']['sources'])
304cflags          = strip_slashes(js['targets']['//:skia']['cflags'])
305cflags_cc       = strip_slashes(js['targets']['//:skia']['cflags_cc'])
306local_includes  = strip_slashes(js['targets']['//:skia']['include_dirs'])
307export_includes = strip_slashes(js['targets']['//:public']['include_dirs'])
308
309dm_srcs         = strip_slashes(js['targets']['//:dm']['sources'])
310dm_includes     = strip_slashes(js['targets']['//:dm']['include_dirs'])
311
312nanobench_target = js['targets']['//:nanobench']
313nanobench_srcs     = strip_slashes(nanobench_target['sources'])
314nanobench_includes = strip_slashes(nanobench_target['include_dirs'])
315
316gn_to_bp_utils.GrabDependentValues(js, '//:dm', 'sources', dm_srcs, 'skia')
317gn_to_bp_utils.GrabDependentValues(js, '//:nanobench', 'sources',
318                                   nanobench_srcs, 'skia')
319
320# skcms is a little special, kind of a second-party library.
321local_includes.add("third_party/skcms")
322dm_includes   .add("third_party/skcms")
323
324# Android's build will choke if we list headers.
325def strip_headers(sources):
326  return {s for s in sources if not s.endswith('.h')}
327
328gn_to_bp_utils.GrabDependentValues(js, '//:skia', 'sources', android_srcs, None)
329android_srcs    = strip_headers(android_srcs)
330
331js_linux        = gn_to_bp_utils.GenerateJSONFromGN(gn_args_linux)
332linux_srcs      = strip_slashes(js_linux['targets']['//:skia']['sources'])
333gn_to_bp_utils.GrabDependentValues(js_linux, '//:skia', 'sources', linux_srcs,
334                                   None)
335linux_srcs      = strip_headers(linux_srcs)
336
337js_mac          = gn_to_bp_utils.GenerateJSONFromGN(gn_args_mac)
338mac_srcs        = strip_slashes(js_mac['targets']['//:skia']['sources'])
339gn_to_bp_utils.GrabDependentValues(js_mac, '//:skia', 'sources', mac_srcs,
340                                   None)
341mac_srcs        = strip_headers(mac_srcs)
342
343srcs = android_srcs.intersection(linux_srcs).intersection(mac_srcs)
344android_srcs    = android_srcs.difference(srcs)
345linux_srcs      =   linux_srcs.difference(srcs)
346mac_srcs        =   mac_srcs.difference(srcs)
347dm_srcs         = strip_headers(dm_srcs)
348nanobench_srcs  = strip_headers(nanobench_srcs)
349
350cflags = gn_to_bp_utils.CleanupCFlags(cflags)
351cflags_cc = gn_to_bp_utils.CleanupCCFlags(cflags_cc)
352
353here = os.path.dirname(__file__)
354defs = gn_to_bp_utils.GetArchSources(os.path.join(here, 'opts.gni'))
355
356def get_defines(json):
357  return {str(d) for d in json['targets']['//:skia']['defines']}
358android_defines = get_defines(js)
359linux_defines   = get_defines(js_linux)
360mac_defines     = get_defines(js_mac)
361
362def mkdir_if_not_exists(path):
363  if not os.path.exists(path):
364    os.mkdir(path)
365mkdir_if_not_exists('include/config/android/')
366mkdir_if_not_exists('include/config/linux/')
367mkdir_if_not_exists('include/config/mac/')
368
369platforms = { 'IOS', 'MAC', 'WIN', 'ANDROID', 'UNIX' }
370
371def disallow_platforms(config, desired):
372  with open(config, 'a') as f:
373    p = sorted(platforms.difference({ desired }))
374    s = '#if '
375    for i in range(len(p)):
376      s = s + 'defined(SK_BUILD_FOR_%s)' % p[i]
377      if i < len(p) - 1:
378        s += ' || '
379        if i % 2 == 1:
380          s += '\\\n    '
381    print >>f, s
382    print >>f, '    #error "Only SK_BUILD_FOR_%s should be defined!"' % desired
383    print >>f, '#endif'
384
385def append_to_file(config, s):
386  with open(config, 'a') as f:
387    print >>f, s
388
389android_config = 'include/config/android/SkUserConfig.h'
390gn_to_bp_utils.WriteUserConfig(android_config, android_defines)
391append_to_file(android_config, '''
392#ifndef SK_BUILD_FOR_ANDROID
393    #error "SK_BUILD_FOR_ANDROID must be defined!"
394#endif''')
395disallow_platforms(android_config, 'ANDROID')
396
397def write_config(config_path, defines, platform):
398  gn_to_bp_utils.WriteUserConfig(config_path, defines)
399  append_to_file(config_path, '''
400// Correct SK_BUILD_FOR flags that may have been set by
401// SkPreConfig.h/Android.bp
402#ifndef SK_BUILD_FOR_%s
403    #define SK_BUILD_FOR_%s
404#endif
405#ifdef SK_BUILD_FOR_ANDROID
406    #undef SK_BUILD_FOR_ANDROID
407#endif''' % (platform, platform))
408  disallow_platforms(config_path, platform)
409
410write_config('include/config/linux/SkUserConfig.h', linux_defines, 'UNIX')
411write_config('include/config/mac/SkUserConfig.h',   mac_defines, 'MAC')
412
413# Turn a list of strings into the style bpfmt outputs.
414def bpfmt(indent, lst, sort=True):
415  if sort:
416    lst = sorted(lst)
417  return ('\n' + ' '*indent).join('"%s",' % v for v in lst)
418
419# OK!  We have everything to fill in Android.bp...
420with open('Android.bp', 'w') as Android_bp:
421  print >>Android_bp, bp.substitute({
422    'export_includes': bpfmt(8, export_includes),
423    'local_includes':  bpfmt(8, local_includes),
424    'srcs':            bpfmt(8, srcs),
425    'cflags':          bpfmt(8, cflags, False),
426    'cflags_cc':       bpfmt(8, cflags_cc),
427
428    'arm_srcs':      bpfmt(16, strip_headers(defs['armv7'])),
429    'arm_neon_srcs': bpfmt(20, strip_headers(defs['neon'])),
430    'arm64_srcs':    bpfmt(16, strip_headers(defs['arm64'] +
431                                             defs['crc32'])),
432    'none_srcs':     bpfmt(16, strip_headers(defs['none'])),
433    'x86_srcs':      bpfmt(16, strip_headers(defs['sse2'] +
434                                             defs['ssse3'] +
435                                             defs['sse41'] +
436                                             defs['sse42'] +
437                                             defs['avx'  ] +
438                                             defs['hsw'  ])),
439
440    'dm_includes'       : bpfmt(8, dm_includes),
441    'dm_srcs'           : bpfmt(8, dm_srcs),
442
443    'nanobench_includes'    : bpfmt(8, nanobench_includes),
444    'nanobench_srcs'        : bpfmt(8, nanobench_srcs),
445
446    'android_srcs':  bpfmt(10, android_srcs),
447    'linux_srcs':    bpfmt(10, linux_srcs),
448    'mac_srcs':      bpfmt(10, mac_srcs),
449  })
450