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
5import default_flavor
6
7"""GN flavor utils, used for building Skia with GN."""
8class GNFlavorUtils(default_flavor.DefaultFlavorUtils):
9  def _run(self, title, cmd, infra_step=False):
10    self.m.run(self.m.step, title, cmd=cmd,
11               infra_step=infra_step)
12
13  def _py(self, title, script, infra_step=True, args=()):
14    self.m.run(self.m.python, title, script=script, args=args,
15               infra_step=infra_step)
16
17  def build_command_buffer(self):
18    self.m.run(self.m.python, 'build command_buffer',
19        script=self.m.vars.skia_dir.join('tools', 'build_command_buffer.py'),
20        args=[
21          '--chrome-dir', self.m.vars.checkout_root,
22          '--output-dir', self.m.vars.skia_out.join(self.m.vars.configuration),
23          '--no-sync', '--make-output-dir'])
24
25  def compile(self, unused_target):
26    """Build Skia with GN."""
27    compiler      = self.m.vars.builder_cfg.get('compiler',      '')
28    configuration = self.m.vars.builder_cfg.get('configuration', '')
29    extra_config  = self.m.vars.builder_cfg.get('extra_config',  '')
30    os            = self.m.vars.builder_cfg.get('os',            '')
31    target_arch   = self.m.vars.builder_cfg.get('target_arch',   '')
32
33    clang_linux   = str(self.m.vars.slave_dir.join('clang_linux'))
34    linux_vulkan_sdk   = str(self.m.vars.slave_dir.join('linux_vulkan_sdk'))
35    win_toolchain = str(self.m.vars.slave_dir.join(
36      't', 'depot_tools', 'win_toolchain', 'vs_files',
37      'd3cb0e37bdd120ad0ac4650b674b09e81be45616'))
38    win_vulkan_sdk = str(self.m.vars.slave_dir.join('win_vulkan_sdk'))
39
40    cc, cxx = None, None
41    extra_cflags = []
42    extra_ldflags = []
43
44    if compiler == 'Clang' and os == 'Ubuntu':
45      cc  = clang_linux + '/bin/clang'
46      cxx = clang_linux + '/bin/clang++'
47      extra_ldflags.append('-fuse-ld=lld')
48    elif compiler == 'Clang':
49      cc, cxx = 'clang', 'clang++'
50    elif compiler == 'GCC':
51      cc, cxx = 'gcc', 'g++'
52
53    if compiler != 'MSVC' and configuration == 'Debug':
54      extra_cflags.append('-O1')
55
56    if extra_config == 'Exceptions':
57      extra_cflags.append('/EHsc')
58    if extra_config == 'Fast':
59      extra_cflags.extend(['-march=native', '-fomit-frame-pointer', '-O3',
60                           '-ffp-contract=off'])
61    if extra_config.startswith('SK'):
62      extra_cflags.append('-D' + extra_config)
63    if extra_config == 'MSAN':
64      extra_ldflags.append('-L' + clang_linux + '/msan')
65
66    args = {}
67
68    if configuration != 'Debug':
69      args['is_debug'] = 'false'
70    if extra_config == 'ANGLE':
71      args['skia_use_angle'] = 'true'
72    if extra_config == 'CommandBuffer':
73      self.m.run.run_once(self.build_command_buffer)
74    if extra_config == 'GDI':
75      args['skia_use_gdi'] = 'true'
76    if extra_config == 'MSAN':
77      args['skia_use_fontconfig'] = 'false'
78    if extra_config == 'ASAN':
79      args['skia_enable_spirv_validation'] = 'false'
80    if extra_config == 'Mesa':
81      args['skia_use_mesa'] = 'true'
82    if extra_config == 'Mini':
83      args.update({
84        'is_component_build':     'true',   # Proves we can link a coherent .so.
85        'is_official_build':      'true',   # No debug symbols, no tools.
86        'skia_enable_effects':    'false',
87        'skia_enable_gpu':        'false',
88        'skia_enable_pdf':        'false',
89        'skia_use_expat':         'false',
90        'skia_use_libjpeg_turbo': 'false',
91        'skia_use_libpng':        'false',
92        'skia_use_libwebp':       'false',
93        'skia_use_zlib':          'false',
94      })
95    if extra_config == 'NoGPU':
96      args['skia_enable_gpu'] = 'false'
97    if extra_config == 'Shared':
98      args['is_component_build'] = 'true'
99    if extra_config == 'Vulkan':
100      args['skia_enable_vulkan_debug_layers'] = 'false'
101      if os == 'Ubuntu':
102        args['skia_vulkan_sdk'] = '"%s"' % linux_vulkan_sdk
103      if 'Win' in os:
104        args['skia_vulkan_sdk'] = '"%s"' % win_vulkan_sdk
105
106    for (k,v) in {
107      'cc':  cc,
108      'cxx': cxx,
109      'sanitize': extra_config if 'SAN' in extra_config else '',
110      'target_cpu': target_arch,
111      'target_os': 'ios' if 'iOS' in extra_config else '',
112      'windk': win_toolchain if 'Win' in os else '',
113    }.iteritems():
114      if v:
115        args[k] = '"%s"' % v
116    if extra_cflags:
117      args['extra_cflags'] = repr(extra_cflags).replace("'", '"')
118    if extra_ldflags:
119      args['extra_ldflags'] = repr(extra_ldflags).replace("'", '"')
120
121    gn_args = ' '.join('%s=%s' % (k,v) for (k,v) in sorted(args.iteritems()))
122
123    gn    = 'gn.exe'    if 'Win' in os else 'gn'
124    ninja = 'ninja.exe' if 'Win' in os else 'ninja'
125    gn = self.m.vars.skia_dir.join('bin', gn)
126
127    with self.m.step.context({'cwd': self.m.vars.skia_dir}):
128      self._py('fetch-gn', self.m.vars.skia_dir.join('bin', 'fetch-gn'))
129      self._run('gn gen', [gn, 'gen', self.out_dir, '--args=' + gn_args])
130      self._run('ninja', [ninja, '-C', self.out_dir])
131
132  def copy_extra_build_products(self, swarming_out_dir):
133    configuration = self.m.vars.builder_cfg.get('configuration', '')
134    extra_config  = self.m.vars.builder_cfg.get('extra_config',  '')
135    os            = self.m.vars.builder_cfg.get('os',            '')
136
137    win_vulkan_sdk = str(self.m.vars.slave_dir.join('win_vulkan_sdk'))
138    if 'Win' in os and extra_config == 'Vulkan':
139      self.m.run.copy_build_products(
140          win_vulkan_sdk,
141          swarming_out_dir.join('out', configuration + '_x64'))
142
143  def step(self, name, cmd):
144    app = self.m.vars.skia_out.join(self.m.vars.configuration, cmd[0])
145    cmd = [app] + cmd[1:]
146    env = self.m.step.get_from_context('env', {})
147
148    clang_linux = str(self.m.vars.slave_dir.join('clang_linux'))
149    extra_config = self.m.vars.builder_cfg.get('extra_config', '')
150
151    if 'SAN' in extra_config:
152      # Sanitized binaries may want to run clang_linux/bin/llvm-symbolizer.
153      env['PATH'] = '%%(PATH)s:%s' % clang_linux + '/bin'
154    elif 'Ubuntu' == self.m.vars.builder_cfg.get('os', ''):
155      cmd = ['catchsegv'] + cmd
156
157    if 'ASAN' == extra_config:
158      env[ 'ASAN_OPTIONS'] = 'symbolize=1 detect_leaks=1'
159      env[ 'LSAN_OPTIONS'] = 'symbolize=1 print_suppressions=1'
160      env['UBSAN_OPTIONS'] = 'symbolize=1 print_stacktrace=1'
161
162    if 'MSAN' == extra_config:
163      # Find the MSAN-built libc++.
164      env['LD_LIBRARY_PATH'] = clang_linux + '/msan'
165
166    to_symbolize = ['dm', 'nanobench']
167    if name in to_symbolize and 'Ubuntu' in self.m.vars.builder_cfg['os']:
168      # Convert path objects or placeholders into strings such that they can
169      # be passed to symbolize_stack_trace.py
170      args = [self.m.vars.slave_dir] + [str(x) for x in cmd]
171      with self.m.step.context({'cwd': self.m.vars.skia_dir, 'env': env}):
172        self._py('symbolized %s' % name,
173                 self.m.vars.infrabots_dir.join('recipe_modules', 'core',
174                 'resources', 'symbolize_stack_trace.py'),
175                 args=args,
176                 infra_step=False)
177
178    else:
179      with self.m.step.context({'env': env}):
180        self._run(name, cmd)
181