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