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