Home | History | Annotate | Download | only in flavor
      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 
      5 
      6 from recipe_engine import recipe_api
      7 
      8 import default_flavor
      9 import gn_flavor
     10 import json
     11 import subprocess
     12 
     13 
     14 """
     15   GN Chromebook flavor utils, used for building and testing Skia for ARM
     16   Chromebooks with GN
     17 """
     18 class GNChromebookFlavorUtils(gn_flavor.GNFlavorUtils):
     19 
     20   def __init__(self, m):
     21     super(GNChromebookFlavorUtils, self).__init__(m)
     22     self._user_ip = ''
     23 
     24     self.device_dirs = default_flavor.DeviceDirs(
     25       dm_dir        = self.m.vars.chromeos_homedir + 'dm_out',
     26       perf_data_dir = self.m.vars.chromeos_homedir + 'perf',
     27       resource_dir  = self.m.vars.chromeos_homedir + 'resources',
     28       images_dir    = self.m.vars.chromeos_homedir + 'images',
     29       skp_dir       = self.m.vars.chromeos_homedir + 'skps',
     30       svg_dir       = self.m.vars.chromeos_homedir + 'svgs',
     31       tmp_dir       = self.m.vars.chromeos_homedir)
     32 
     33     self._bin_dir = self.m.vars.chromeos_homedir + 'bin'
     34 
     35   @property
     36   def user_ip(self):
     37     if not self._user_ip:
     38       ssh_info = self.m.run(self.m.python.inline, 'read chromeos ip',
     39                             program="""
     40       import os
     41       SSH_MACHINE_FILE = os.path.expanduser('~/ssh_machine.json')
     42       with open(SSH_MACHINE_FILE, 'r') as f:
     43         print f.read()
     44       """,
     45       stdout=self.m.raw_io.output(),
     46       infra_step=True).stdout
     47 
     48       self._user_ip = json.loads(ssh_info).get(u'user_ip', 'ERROR')
     49     return self._user_ip
     50 
     51   def _ssh(self, title, *cmd, **kwargs):
     52     if 'infra_step' not in kwargs:
     53       kwargs['infra_step'] = True
     54 
     55     ssh_cmd = ['ssh', '-oConnectTimeout=15', '-oBatchMode=yes',
     56                '-t', '-t', self.user_ip] + list(cmd)
     57 
     58     return self._run(title, ssh_cmd, **kwargs)
     59 
     60   def install(self):
     61     self._ssh('mkdir %s' % self.device_dirs.resource_dir, 'mkdir', '-p',
     62               self.device_dirs.resource_dir)
     63 
     64     # Ensure the home dir is marked executable
     65     self._ssh('remount %s as exec' % self.m.vars.chromeos_homedir,
     66               'sudo', 'mount', '-i', '-o', 'remount,exec', '/home/chronos')
     67 
     68     self.create_clean_device_dir(self._bin_dir)
     69 
     70   def compile(self, unused_target):
     71     configuration = self.m.vars.builder_cfg.get('configuration')
     72     os            = self.m.vars.builder_cfg.get('os')
     73     target_arch   = self.m.vars.builder_cfg.get('target_arch')
     74 
     75     clang_linux = self.m.vars.slave_dir.join('clang_linux')
     76     # This is a pretty typical arm-linux-gnueabihf sysroot
     77     sysroot_dir = self.m.vars.slave_dir.join('armhf_sysroot')
     78 
     79     if 'arm' == target_arch:
     80       # This is the extra things needed to link against for the chromebook.
     81       #  For example, the Mali GL drivers.
     82       gl_dir = self.m.vars.slave_dir.join('chromebook_arm_gles')
     83       env = {'LD_LIBRARY_PATH': sysroot_dir.join('lib')}
     84       extra_asmflags = [
     85         '--target=armv7a-linux-gnueabihf',
     86         '--sysroot=%s' % sysroot_dir,
     87         '-march=armv7-a',
     88         '-mfpu=neon',
     89         '-mthumb',
     90       ]
     91 
     92       extra_cflags = [
     93         '--target=armv7a-linux-gnueabihf',
     94         '--sysroot=%s' % sysroot_dir,
     95         '-I%s' % gl_dir.join('include'),
     96         '-I%s' % sysroot_dir.join('include'),
     97         '-I%s' % sysroot_dir.join('include', 'c++', '4.8.4'),
     98         '-I%s' % sysroot_dir.join('include', 'c++', '4.8.4',
     99                                   'arm-linux-gnueabihf'),
    100         '-DMESA_EGL_NO_X11_HEADERS',
    101       ]
    102 
    103       extra_ldflags = [
    104         '--target=armv7a-linux-gnueabihf',
    105         '--sysroot=%s' % sysroot_dir,
    106         # use sysroot's ld which can properly link things.
    107         '-B%s' % sysroot_dir.join('bin'),
    108         # helps locate crt*.o
    109         '-B%s' % sysroot_dir.join('gcc-cross'),
    110         # helps locate libgcc*.so
    111         '-L%s' % sysroot_dir.join('gcc-cross'),
    112         '-L%s' % sysroot_dir.join('lib'),
    113         '-L%s' % gl_dir.join('lib'),
    114         # Explicitly do not use lld for cross compiling like this - I observed
    115         # failures like "Unrecognized reloc 41" and couldn't find out why.
    116       ]
    117     else:
    118       gl_dir = self.m.vars.slave_dir.join('chromebook_x86_64_gles')
    119       env = {}
    120       extra_asmflags = []
    121       extra_cflags = [
    122         '-DMESA_EGL_NO_X11_HEADERS',
    123         '-I%s' % gl_dir.join('include'),
    124       ]
    125       extra_ldflags = [
    126         '-L%s' % gl_dir.join('lib'),
    127         '-static-libstdc++', '-static-libgcc',
    128         '-fuse-ld=lld',
    129       ]
    130 
    131     quote = lambda x: '"%s"' % x
    132     args = {
    133       'cc': quote(clang_linux.join('bin','clang')),
    134       'cxx': quote(clang_linux.join('bin','clang++')),
    135       'target_cpu': quote(target_arch),
    136       'skia_use_fontconfig': 'false',
    137       'skia_use_system_freetype2': 'false',
    138       'skia_use_egl': 'true',
    139     }
    140 
    141     if configuration != 'Debug':
    142       args['is_debug'] = 'false'
    143     args['extra_asmflags'] = repr(extra_asmflags).replace("'", '"')
    144     args['extra_cflags'] = repr(extra_cflags).replace("'", '"')
    145     args['extra_ldflags'] = repr(extra_ldflags).replace("'", '"')
    146 
    147     gn_args = ' '.join('%s=%s' % (k,v) for (k,v) in sorted(args.iteritems()))
    148 
    149     gn    = 'gn.exe'    if 'Win' in os else 'gn'
    150     ninja = 'ninja.exe' if 'Win' in os else 'ninja'
    151     gn = self.m.vars.skia_dir.join('bin', gn)
    152 
    153     with self.m.context(cwd=self.m.vars.skia_dir,
    154                         env=env):
    155       self._py('fetch-gn', self.m.vars.skia_dir.join('bin', 'fetch-gn'))
    156       self._run('gn gen', [gn, 'gen', self.out_dir, '--args=' + gn_args])
    157       self._run('ninja', [ninja, '-k', '0'
    158                                , '-C', self.out_dir
    159                                , 'nanobench', 'dm'])
    160 
    161   def create_clean_device_dir(self, path):
    162     # use -f to silently return if path doesn't exist
    163     self._ssh('rm %s' % path, 'rm', '-rf', path)
    164     self._ssh('mkdir %s' % path, 'mkdir', '-p', path)
    165 
    166   def read_file_on_device(self, path, **kwargs):
    167     rv = self._ssh('read %s' % path,
    168                    'cat', path, stdout=self.m.raw_io.output(),
    169                    **kwargs)
    170     return rv.stdout.rstrip() if rv and rv.stdout else None
    171 
    172   def remove_file_on_device(self, path):
    173     # use -f to silently return if path doesn't exist
    174     self._ssh('rm %s' % path, 'rm', '-f', path)
    175 
    176   def _prefix_device_path(self, device_path):
    177     return '%s:%s' % (self.user_ip, device_path)
    178 
    179   def copy_file_to_device(self, host_path, device_path):
    180     device_path = self._prefix_device_path(device_path)
    181     # Recipe
    182     self.m.python.inline(str('scp %s %s' % (host_path, device_path)),
    183     """
    184     import subprocess
    185     import sys
    186     host = sys.argv[1]
    187     device   = sys.argv[2]
    188     print subprocess.check_output(['scp', host, device])
    189     """, args=[host_path, device_path], infra_step=True)
    190 
    191   def _copy_dir(self, src, dest):
    192     # We can't use rsync to communicate with the chromebooks because the
    193     # chromebooks don't have rsync installed on them.
    194     self.m.python.inline(str('scp -r %s %s' % (src, dest)),
    195     """
    196     import subprocess
    197     import sys
    198     src = sys.argv[1] + '/*'
    199     dest   = sys.argv[2]
    200     print subprocess.check_output('scp -r %s %s' % (src, dest), shell=True)
    201     """, args=[src, dest], infra_step=True)
    202 
    203   def copy_directory_contents_to_device(self, host_path, device_path):
    204     self._copy_dir(host_path, self._prefix_device_path(device_path))
    205 
    206   def copy_directory_contents_to_host(self, device_path, host_path):
    207     self._copy_dir(self._prefix_device_path(device_path), host_path)
    208 
    209   def step(self, name, cmd, **kwargs):
    210     # Push and run either dm or nanobench
    211 
    212     name = cmd[0]
    213 
    214     if name == 'dm':
    215       self.create_clean_host_dir(self.m.vars.dm_dir)
    216     if name == 'nanobench':
    217       self.create_clean_host_dir(self.m.vars.perf_data_dir)
    218 
    219     app = self.m.vars.skia_out.join(self.m.vars.configuration, cmd[0])
    220 
    221     cmd[0] = '%s/%s' % (self._bin_dir, cmd[0])
    222     self.copy_file_to_device(app, cmd[0])
    223 
    224     self._ssh('chmod %s' % name, 'chmod', '+x', cmd[0])
    225     self._ssh(str(name), *cmd)
    226