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      windows: {
132        cflags: [
133          "-mssse3",
134          "-Wno-unknown-pragmas",
135        ],
136        srcs: [
137          $win_srcs
138        ],
139        local_include_dirs: [
140          "include/config/win",
141        ],
142        export_include_dirs: [
143          "include/config/win",
144        ],
145      },
146    },
147
148    defaults: ["skia_deps",
149               "skia_pgo",
150    ],
151}
152
153// Build libskia with PGO by default.
154// Location of PGO profile data is defined in build/soong/cc/pgo.go
155// and is separate from skia.
156// To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable
157// or set enable_profile_use property to false.
158cc_defaults {
159    name: "skia_pgo",
160    pgo: {
161        instrumentation: true,
162        profile_file: "hwui/hwui.profdata",
163        benchmarks: ["hwui", "skia"],
164        enable_profile_use: true,
165    },
166}
167
168// "defaults" property to disable profile use for Skia tools and benchmarks.
169cc_defaults {
170    name: "skia_pgo_no_profile_use",
171    defaults: [
172        "skia_pgo",
173    ],
174    pgo: {
175        enable_profile_use: false,
176    },
177}
178
179cc_defaults {
180    name: "skia_deps",
181    shared_libs: [
182        "libandroidicu",
183        "libdng_sdk",
184        "libexpat",
185        "libft2",
186        "libjpeg",
187        "liblog",
188        "libpiex",
189        "libpng",
190        "libz",
191    ],
192    static_libs: [
193        "libarect",
194        "libsfntly",
195        "libwebp-decode",
196        "libwebp-encode",
197    ],
198    group_static_libs: true,
199    target: {
200      android: {
201        shared_libs: [
202            "libcutils",
203            "libEGL",
204            "libGLESv2",
205            "libheif",
206            "libvulkan",
207            "libnativewindow",
208        ],
209        export_shared_lib_headers: [
210            "libvulkan",
211        ],
212      },
213      host: {
214        static_libs: [
215          "libcutils",
216        ],
217      },
218      darwin: {
219        host_ldlibs: [
220            "-framework AppKit",
221        ],
222      },
223      windows: {
224        // clang-r353983 emits error when building Skia for Windows. Do not
225        // build it for now until the compiler issue is addressed.
226        // enabled: true,
227        host_ldlibs: [
228            "-lgdi32",
229            "-loleaut32",
230            "-lole32",
231            "-lopengl32",
232            "-luuid",
233            "-lwindowscodecs",
234        ],
235      },
236    },
237}
238
239cc_defaults {
240    name: "skia_tool_deps",
241    defaults: [
242        "skia_deps",
243        "skia_pgo_no_profile_use"
244    ],
245    static_libs: [
246        "libskia",
247    ],
248    cflags: [
249        "-Wno-implicit-fallthrough",
250        "-Wno-unused-parameter",
251        "-Wno-unused-variable",
252    ],
253}
254
255cc_test {
256    name: "skia_dm",
257
258    defaults: [
259        "skia_tool_deps"
260    ],
261
262    local_include_dirs: [
263        $dm_includes
264    ],
265
266    srcs: [
267        $dm_srcs
268    ],
269
270    shared_libs: [
271        "libbinder",
272        "libutils",
273    ],
274}
275
276cc_test {
277    name: "skia_nanobench",
278
279    defaults: [
280        "skia_tool_deps"
281    ],
282
283    local_include_dirs: [
284        $nanobench_includes
285    ],
286
287    srcs: [
288        $nanobench_srcs
289    ],
290
291    data: [
292        "resources/*",
293    ],
294}''')
295
296# We'll run GN to get the main source lists and include directories for Skia.
297def generate_args(target_os, enable_gpu):
298  d = {
299    'is_official_build':                  'true',
300
301    # gn_to_bp_utils' GetArchSources will take care of architecture-specific
302    # files.
303    'target_cpu':                         '"none"',
304
305    # Use the custom FontMgr, as the framework will handle fonts.
306    'skia_enable_fontmgr_custom':         'false',
307    'skia_enable_fontmgr_custom_empty':   'true',
308    'skia_enable_fontmgr_android':        'false',
309    'skia_enable_fontmgr_win':            'false',
310    'skia_enable_fontmgr_win_gdi':        'false',
311    'skia_use_fonthost_mac':              'false',
312
313    'skia_use_freetype':                  'true',
314    'skia_use_fontconfig':                'false',
315    'skia_use_fixed_gamma_text':          'true',
316  }
317  d['target_os'] = target_os
318  if target_os == '"android"':
319    d['skia_enable_tools'] = 'true'
320    d['skia_use_libheif']  = 'true'
321  else:
322    d['skia_use_libheif']  = 'false'
323
324  if enable_gpu:
325    d['skia_use_vulkan']   = 'true'
326  else:
327    d['skia_use_vulkan']   = 'false'
328    d['skia_enable_gpu']   = 'false'
329
330  if target_os == '"win"':
331    # The Android Windows build system does not provide FontSub.h
332    d['skia_use_xps'] = 'false'
333
334    # BUILDCONFIG.gn expects these to be set when building for Windows, but
335    # we're just creating Android.bp, so we don't need them. Populate with
336    # some dummy values.
337    d['win_vc'] = '"dummy_version"'
338    d['win_sdk_version'] = '"dummy_version"'
339  return d
340
341gn_args       = generate_args('"android"', True)
342gn_args_linux = generate_args('"linux"',   False)
343gn_args_mac   = generate_args('"mac"',     False)
344gn_args_win   = generate_args('"win"',     False)
345
346js = gn_to_bp_utils.GenerateJSONFromGN(gn_args)
347
348def strip_slashes(lst):
349  return {str(p.lstrip('/')) for p in lst}
350
351android_srcs    = strip_slashes(js['targets']['//:skia']['sources'])
352cflags          = strip_slashes(js['targets']['//:skia']['cflags'])
353cflags_cc       = strip_slashes(js['targets']['//:skia']['cflags_cc'])
354local_includes  = strip_slashes(js['targets']['//:skia']['include_dirs'])
355export_includes = strip_slashes(js['targets']['//:public']['include_dirs'])
356
357dm_srcs         = strip_slashes(js['targets']['//:dm']['sources'])
358dm_includes     = strip_slashes(js['targets']['//:dm']['include_dirs'])
359
360nanobench_target = js['targets']['//:nanobench']
361nanobench_srcs     = strip_slashes(nanobench_target['sources'])
362nanobench_includes = strip_slashes(nanobench_target['include_dirs'])
363
364gn_to_bp_utils.GrabDependentValues(js, '//:dm', 'sources', dm_srcs, 'skia')
365gn_to_bp_utils.GrabDependentValues(js, '//:nanobench', 'sources',
366                                   nanobench_srcs, 'skia')
367
368# skcms is a little special, kind of a second-party library.
369local_includes.add("third_party/skcms")
370dm_includes   .add("third_party/skcms")
371
372# Android's build will choke if we list headers.
373def strip_headers(sources):
374  return {s for s in sources if not s.endswith('.h')}
375
376gn_to_bp_utils.GrabDependentValues(js, '//:skia', 'sources', android_srcs, None)
377android_srcs    = strip_headers(android_srcs)
378
379js_linux        = gn_to_bp_utils.GenerateJSONFromGN(gn_args_linux)
380linux_srcs      = strip_slashes(js_linux['targets']['//:skia']['sources'])
381gn_to_bp_utils.GrabDependentValues(js_linux, '//:skia', 'sources', linux_srcs,
382                                   None)
383linux_srcs      = strip_headers(linux_srcs)
384
385js_mac          = gn_to_bp_utils.GenerateJSONFromGN(gn_args_mac)
386mac_srcs        = strip_slashes(js_mac['targets']['//:skia']['sources'])
387gn_to_bp_utils.GrabDependentValues(js_mac, '//:skia', 'sources', mac_srcs,
388                                   None)
389mac_srcs        = strip_headers(mac_srcs)
390
391js_win          = gn_to_bp_utils.GenerateJSONFromGN(gn_args_win)
392win_srcs        = strip_slashes(js_win['targets']['//:skia']['sources'])
393gn_to_bp_utils.GrabDependentValues(js_win, '//:skia', 'sources', win_srcs,
394                                   None)
395win_srcs        = strip_headers(win_srcs)
396
397srcs = android_srcs.intersection(linux_srcs).intersection(mac_srcs)
398srcs = srcs.intersection(win_srcs)
399android_srcs    = android_srcs.difference(srcs)
400linux_srcs      =   linux_srcs.difference(srcs)
401mac_srcs        =     mac_srcs.difference(srcs)
402win_srcs        =     win_srcs.difference(srcs)
403dm_srcs         = strip_headers(dm_srcs)
404nanobench_srcs  = strip_headers(nanobench_srcs)
405
406cflags = gn_to_bp_utils.CleanupCFlags(cflags)
407cflags_cc = gn_to_bp_utils.CleanupCCFlags(cflags_cc)
408
409here = os.path.dirname(__file__)
410defs = gn_to_bp_utils.GetArchSources(os.path.join(here, 'opts.gni'))
411
412def get_defines(json):
413  return {str(d) for d in json['targets']['//:skia']['defines']}
414android_defines = get_defines(js)
415linux_defines   = get_defines(js_linux)
416mac_defines     = get_defines(js_mac)
417win_defines     = get_defines(js_win)
418
419def mkdir_if_not_exists(path):
420  if not os.path.exists(path):
421    os.mkdir(path)
422mkdir_if_not_exists('include/config/android/')
423mkdir_if_not_exists('include/config/linux/')
424mkdir_if_not_exists('include/config/mac/')
425mkdir_if_not_exists('include/config/win/')
426
427platforms = { 'IOS', 'MAC', 'WIN', 'ANDROID', 'UNIX' }
428
429def disallow_platforms(config, desired):
430  with open(config, 'a') as f:
431    p = sorted(platforms.difference({ desired }))
432    s = '#if '
433    for i in range(len(p)):
434      s = s + 'defined(SK_BUILD_FOR_%s)' % p[i]
435      if i < len(p) - 1:
436        s += ' || '
437        if i % 2 == 1:
438          s += '\\\n    '
439    print >>f, s
440    print >>f, '    #error "Only SK_BUILD_FOR_%s should be defined!"' % desired
441    print >>f, '#endif'
442
443def append_to_file(config, s):
444  with open(config, 'a') as f:
445    print >>f, s
446
447android_config = 'include/config/android/SkUserConfig.h'
448gn_to_bp_utils.WriteUserConfig(android_config, android_defines)
449append_to_file(android_config, '''
450#ifndef SK_BUILD_FOR_ANDROID
451    #error "SK_BUILD_FOR_ANDROID must be defined!"
452#endif''')
453disallow_platforms(android_config, 'ANDROID')
454
455def write_config(config_path, defines, platform):
456  gn_to_bp_utils.WriteUserConfig(config_path, defines)
457  append_to_file(config_path, '''
458// Correct SK_BUILD_FOR flags that may have been set by
459// SkPreConfig.h/Android.bp
460#ifndef SK_BUILD_FOR_%s
461    #define SK_BUILD_FOR_%s
462#endif
463#ifdef SK_BUILD_FOR_ANDROID
464    #undef SK_BUILD_FOR_ANDROID
465#endif''' % (platform, platform))
466  disallow_platforms(config_path, platform)
467
468write_config('include/config/linux/SkUserConfig.h', linux_defines, 'UNIX')
469write_config('include/config/mac/SkUserConfig.h',   mac_defines, 'MAC')
470write_config('include/config/win/SkUserConfig.h',   win_defines, 'WIN')
471
472# Turn a list of strings into the style bpfmt outputs.
473def bpfmt(indent, lst, sort=True):
474  if sort:
475    lst = sorted(lst)
476  return ('\n' + ' '*indent).join('"%s",' % v for v in lst)
477
478# OK!  We have everything to fill in Android.bp...
479with open('Android.bp', 'w') as Android_bp:
480  print >>Android_bp, bp.substitute({
481    'export_includes': bpfmt(8, export_includes),
482    'local_includes':  bpfmt(8, local_includes),
483    'srcs':            bpfmt(8, srcs),
484    'cflags':          bpfmt(8, cflags, False),
485    'cflags_cc':       bpfmt(8, cflags_cc),
486
487    'arm_srcs':      bpfmt(16, strip_headers(defs['armv7'])),
488    'arm_neon_srcs': bpfmt(20, strip_headers(defs['neon'])),
489    'arm64_srcs':    bpfmt(16, strip_headers(defs['arm64'] +
490                                             defs['crc32'])),
491    'none_srcs':     bpfmt(16, strip_headers(defs['none'])),
492    'x86_srcs':      bpfmt(16, strip_headers(defs['sse2'] +
493                                             defs['ssse3'] +
494                                             defs['sse41'] +
495                                             defs['sse42'] +
496                                             defs['avx'  ] +
497                                             defs['hsw'  ])),
498
499    'dm_includes'       : bpfmt(8, dm_includes),
500    'dm_srcs'           : bpfmt(8, dm_srcs),
501
502    'nanobench_includes'    : bpfmt(8, nanobench_includes),
503    'nanobench_srcs'        : bpfmt(8, nanobench_srcs),
504
505    'android_srcs':  bpfmt(10, android_srcs),
506    'linux_srcs':    bpfmt(10, linux_srcs),
507    'mac_srcs':      bpfmt(10, mac_srcs),
508    'win_srcs':      bpfmt(10, win_srcs),
509  })
510