blob: d1b8f22030c41838f480c58f48c2a4e8c3ef962e [file] [log] [blame]
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from recipe_engine import recipe_api
import default_flavor
import gn_android_flavor
import subprocess
"""GN Chromecast flavor utils, used for building Skia for Chromecast with GN"""
class GNChromecastFlavorUtils(gn_android_flavor.GNAndroidFlavorUtils):
def __init__(self, m):
super(GNChromecastFlavorUtils, self).__init__(m)
self._ever_ran_adb = False
self._user_ip = ''
self.m.vars.android_bin_dir = self.m.path.join(self.m.vars.android_bin_dir,
'bin')
# Disk space is extremely tight on the Chromecasts (~100M) There is not
# enough space on the android_data_dir (/cache/skia) to fit the images,
# resources, executable and output the dm images. So, we have dm_out be
# on the tempfs (i.e. RAM) /dev/shm. (which is about 140M)
self.device_dirs = default_flavor.DeviceDirs(
dm_dir = '/dev/shm/skia/dm_out',
perf_data_dir = self.m.vars.android_data_dir + 'perf',
resource_dir = self.m.vars.android_data_dir + 'resources',
images_dir = self.m.vars.android_data_dir + 'images',
skp_dir = self.m.vars.android_data_dir + 'skps',
svg_dir = self.m.vars.android_data_dir + 'svgs',
tmp_dir = self.m.vars.android_data_dir)
@property
def user_ip_host(self):
if not self._user_ip:
self._user_ip = self.m.run(self.m.python.inline, 'read chromecast ip',
program="""
import os
CHROMECAST_IP_FILE = os.path.expanduser('~/chromecast.txt')
with open(CHROMECAST_IP_FILE, 'r') as f:
print f.read()
""",
stdout=self.m.raw_io.output(),
infra_step=True).stdout
return self._user_ip
@property
def user_ip(self):
return self.user_ip_host.split(':')[0]
def compile(self, unused_target):
configuration = self.m.vars.builder_cfg.get('configuration')
os = self.m.vars.builder_cfg.get('os')
target_arch = self.m.vars.builder_cfg.get('target_arch')
# TODO(kjlubick): can this toolchain be replaced/shared with chromebook?
toolchain_dir = self.m.vars.slave_dir.join('cast_toolchain', 'armv7a')
gles_dir = self.m.vars.slave_dir.join('chromebook_arm_gles')
extra_cflags = [
'-I%s' % gles_dir.join('include'),
'-DMESA_EGL_NO_X11_HEADERS',
"-DSK_NO_COMMAND_BUFFER",
# Avoid unused warning with yyunput
'-Wno-error=unused-function',
# Makes the binary small enough to fit on the small disk.
'-g0',
('-DDUMMY_cast_toolchain_version=%s' %
self.m.run.asset_version('cast_toolchain')),
]
extra_ldflags = [
# Chromecast does not package libstdc++
'-static-libstdc++', '-static-libgcc',
'-L%s' % toolchain_dir.join('lib'),
]
quote = lambda x: '"%s"' % x
args = {
'cc': quote(toolchain_dir.join('bin','armv7a-cros-linux-gnueabi-gcc')),
'cxx': quote(toolchain_dir.join('bin','armv7a-cros-linux-gnueabi-g++')),
'ar': quote(toolchain_dir.join('bin','armv7a-cros-linux-gnueabi-ar')),
'target_cpu': quote(target_arch),
'skia_use_fontconfig': 'false',
'skia_enable_gpu': 'true',
# The toolchain won't allow system libraries to be used
# when cross-compiling
'skia_use_system_freetype2': 'false',
# Makes the binary smaller
'skia_use_icu': 'false',
'skia_use_egl': 'true',
}
if configuration != 'Debug':
args['is_debug'] = 'false'
args['extra_cflags'] = repr(extra_cflags).replace("'", '"')
args['extra_ldflags'] = repr(extra_ldflags).replace("'", '"')
gn_args = ' '.join('%s=%s' % (k,v) for (k,v) in sorted(args.iteritems()))
gn = 'gn.exe' if 'Win' in os else 'gn'
ninja = 'ninja.exe' if 'Win' in os else 'ninja'
gn = self.m.vars.skia_dir.join('bin', gn)
self._py('fetch-gn', self.m.vars.skia_dir.join('bin', 'fetch-gn'))
self._run('gn gen', gn, 'gen', self.out_dir, '--args=' + gn_args)
self._run('ninja', ninja, '-k', '0', '-C', self.out_dir, 'nanobench', 'dm')
def install(self):
super(GNChromecastFlavorUtils, self).install()
self._adb('mkdir ' + self.m.vars.android_bin_dir,
'shell', 'mkdir', '-p', self.m.vars.android_bin_dir)
def _adb(self, title, *cmd, **kwargs):
if not self._ever_ran_adb:
self._connect_to_remote()
self._ever_ran_adb = True
# The only non-infra adb steps (dm / nanobench) happen to not use _adb().
if 'infra_step' not in kwargs:
kwargs['infra_step'] = True
return self._run(title, 'adb', *cmd, **kwargs)
def _connect_to_remote(self):
self.m.run(self.m.step, 'adb connect %s' % self.user_ip_host, cmd=['adb',
'connect', self.user_ip_host], infra_step=True)
def create_clean_device_dir(self, path):
# Note: Chromecast does not support -rf
self._adb('rm %s' % path, 'shell', 'rm', '-r', path)
self._adb('mkdir %s' % path, 'shell', 'mkdir', '-p', path)
def copy_directory_contents_to_device(self, host, device):
# Copy the tree, avoiding hidden directories and resolving symlinks.
# Additionally, due to space restraints, we don't push files > 3 MB
# which cuts down the size of the SKP asset to be around 50 MB as of
# version 41.
self.m.run(self.m.python.inline, 'push %s/* %s' % (host, device),
program="""
import os
import subprocess
import sys
host = sys.argv[1]
device = sys.argv[2]
for d, _, fs in os.walk(host):
p = os.path.relpath(d, host)
if p != '.' and p.startswith('.'):
continue
for f in fs:
print os.path.join(p,f)
hp = os.path.realpath(os.path.join(host, p, f))
if os.stat(hp).st_size > (1.5 * 1024 * 1024):
print "Skipping because it is too big"
else:
subprocess.check_call(['adb', 'push',
hp, os.path.join(device, p, f)])
""", args=[host, device], infra_step=True)
def cleanup_steps(self):
if self._ever_ran_adb:
# To clean up disk space for next time
self._ssh('Delete executables', 'rm', '-r', self.m.vars.android_bin_dir,
abort_on_failure=False, infra_step=True)
# Reconnect if was disconnected
self._adb('disconnect', 'disconnect')
self._connect_to_remote()
self.m.run(self.m.python.inline, 'dump log', program="""
import os
import subprocess
import sys
out = sys.argv[1]
log = subprocess.check_output(['adb', 'logcat', '-d'])
for line in log.split('\\n'):
tokens = line.split()
if len(tokens) == 11 and tokens[-7] == 'F' and tokens[-3] == 'pc':
addr, path = tokens[-2:]
local = os.path.join(out, os.path.basename(path))
if os.path.exists(local):
sym = subprocess.check_output(['addr2line', '-Cfpe', local, addr])
line = line.replace(addr, addr + ' ' + sym.strip())
print line
""",
args=[self.m.vars.skia_out.join(self.m.vars.configuration)],
infra_step=True,
abort_on_failure=False)
self._adb('disconnect', 'disconnect')
self._adb('kill adb server', 'kill-server')
def _ssh(self, title, *cmd, **kwargs):
ssh_cmd = ['ssh', '-oConnectTimeout=15', '-oBatchMode=yes',
'-t', '-t', 'root@%s' % self.user_ip] + list(cmd)
return self.m.run(self.m.step, title, cmd=ssh_cmd, **kwargs)
def step(self, name, cmd, **kwargs):
app = self.m.vars.skia_out.join(self.m.vars.configuration, cmd[0])
self._adb('push %s' % cmd[0],
'push', app, self.m.vars.android_bin_dir)
cmd[0] = '%s/%s' % (self.m.vars.android_bin_dir, cmd[0])
self._ssh(str(name), *cmd, infra_step=False)