1# Copyright 2016 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from recipe_engine import recipe_api
6
7import default_flavor
8import gn_android_flavor
9import subprocess
10
11
12"""GN Chromecast flavor utils, used for building Skia for Chromecast with GN"""
13class GNChromecastFlavorUtils(gn_android_flavor.GNAndroidFlavorUtils):
14  def __init__(self, m):
15    super(GNChromecastFlavorUtils, self).__init__(m)
16    self._ever_ran_adb = False
17
18  def compile(self, unused_target):
19    configuration = self.m.vars.builder_cfg.get('configuration')
20    os            = self.m.vars.builder_cfg.get('os')
21    target_arch   = self.m.vars.builder_cfg.get('target_arch')
22
23    # Makes the binary small enough to fit on the small disk.
24    extra_cflags = ['-g0']
25    # Chromecast does not package libstdc++
26    extra_ldflags = ['-static-libstdc++', '-static-libgcc']
27
28    toolchain_dir = self.m.vars.slave_dir.join('cast_toolchain')
29
30    quote = lambda x: '"%s"' % x
31    args = {
32      'cc': quote(toolchain_dir.join('bin','armv7a-cros-linux-gnueabi-gcc')),
33      'cxx': quote(toolchain_dir.join('bin','armv7a-cros-linux-gnueabi-g++')),
34      'ar': quote(toolchain_dir.join('bin','armv7a-cros-linux-gnueabi-ar')),
35      'target_cpu': quote(target_arch),
36      'skia_use_fontconfig': 'false',
37      'skia_enable_gpu': 'false',
38      # The toolchain won't allow system libraries to be used
39      # when cross-compiling
40      'skia_use_system_freetype2': 'false',
41      # Makes the binary smaller
42      'skia_use_icu': 'false',
43    }
44
45    if configuration != 'Debug':
46      args['is_debug'] = 'false'
47    args['extra_cflags'] = repr(extra_cflags).replace("'", '"')
48    args['extra_ldflags'] = repr(extra_ldflags).replace("'", '"')
49
50    gn_args = ' '.join('%s=%s' % (k,v) for (k,v) in sorted(args.iteritems()))
51
52    gn    = 'gn.exe'    if 'Win' in os else 'gn'
53    ninja = 'ninja.exe' if 'Win' in os else 'ninja'
54    gn = self.m.vars.skia_dir.join('bin', gn)
55
56    self._py('fetch-gn', self.m.vars.skia_dir.join('bin', 'fetch-gn'))
57    self._run('gn gen', gn, 'gen', self.out_dir, '--args=' + gn_args)
58    # We only build perf for the chromecasts.
59    self._run('ninja', ninja, '-C', self.out_dir, 'nanobench')
60
61  def _adb(self, title, *cmd, **kwargs):
62    if not self._ever_ran_adb:
63      self._connect_to_remote()
64
65    self._ever_ran_adb = True
66    # The only non-infra adb steps (dm / nanobench) happen to not use _adb().
67    if 'infra_step' not in kwargs:
68      kwargs['infra_step'] = True
69    return self._run(title, 'adb', *cmd, **kwargs)
70
71  def _connect_to_remote(self):
72
73    ip_address = self.m.run(self.m.python.inline, 'read chromecast ip',
74               program="""
75    import os
76    CHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')
77    with open(CHROMECAST_IP_FILE, 'r') as f:
78      print f.read()
79    """,
80    stdout=self.m.raw_io.output(),
81    infra_step=True).stdout
82
83    self.m.run(self.m.step, 'adb connect %s' % ip_address, cmd=['adb',
84      'connect', ip_address], infra_step=True)
85
86  def create_clean_device_dir(self, path):
87    # Note: Chromecast does not support -rf
88    self._adb('rm %s' % path, 'shell', 'rm', '-r', path)
89    self._adb('mkdir %s' % path, 'shell', 'mkdir', '-p', path)
90
91  def copy_directory_contents_to_device(self, host, device):
92    # Copy the tree, avoiding hidden directories and resolving symlinks.
93    # Additionally, due to space restraints, we don't push files > 3 MB
94    # which cuts down the size of the SKP asset to be around 50 MB as of
95    # version 41.
96    self.m.run(self.m.python.inline, 'push %s/* %s' % (host, device),
97               program="""
98    import os
99    import subprocess
100    import sys
101    host   = sys.argv[1]
102    device = sys.argv[2]
103    for d, _, fs in os.walk(host):
104      p = os.path.relpath(d, host)
105      if p != '.' and p.startswith('.'):
106        continue
107      for f in fs:
108        print os.path.join(p,f)
109        hp = os.path.realpath(os.path.join(host, p, f))
110        if os.stat(hp).st_size > (3 * 1024 * 1024):
111          print "Skipping because it is too big"
112        else:
113          subprocess.check_call(['adb', 'push',
114                                hp, os.path.join(device, p, f)])
115    """, args=[host, device], infra_step=True)
116