• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018 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
6from . import util
7
8
9def build_command_buffer(api, chrome_dir, skia_dir, out):
10  api.run(api.python, 'build command_buffer',
11      script=skia_dir.join('tools', 'build_command_buffer.py'),
12      args=[
13        '--chrome-dir', chrome_dir,
14        '--output-dir', out,
15        '--extra-gn-args', 'mac_sdk_min="10.13"',
16        '--no-sync', '--no-hooks', '--make-output-dir'])
17
18
19def compile_swiftshader(api, extra_tokens, swiftshader_root, cc, cxx, out):
20  """Build SwiftShader with CMake.
21
22  Building SwiftShader works differently from any other Skia third_party lib.
23  See discussion in skia:7671 for more detail.
24
25  Args:
26    swiftshader_root: root of the SwiftShader checkout.
27    cc, cxx: compiler binaries to use
28    out: target directory for libEGL.so and libGLESv2.so
29  """
30  swiftshader_opts = [
31      '-DSWIFTSHADER_BUILD_TESTS=OFF',
32      '-DSWIFTSHADER_WARNINGS_AS_ERRORS=OFF',
33      '-DREACTOR_ENABLE_MEMORY_SANITIZER_INSTRUMENTATION=OFF',  # Way too slow.
34  ]
35  cmake_bin = str(api.vars.workdir.join('cmake_linux', 'bin'))
36  env = {
37      'CC': cc,
38      'CXX': cxx,
39      'PATH': '%%(PATH)s:%s' % cmake_bin,
40      # We arrange our MSAN/TSAN prebuilts a little differently than
41      # SwiftShader's CMakeLists.txt expects, so we'll just keep our custom
42      # setup (everything mentioning libcxx below) and point SwiftShader's
43      # CMakeLists.txt at a harmless non-existent path.
44      'SWIFTSHADER_MSAN_INSTRUMENTED_LIBCXX_PATH': '/totally/phony/path',
45  }
46
47  # Extra flags for MSAN/TSAN, if necessary.
48  san = None
49  if 'MSAN' in extra_tokens:
50    san = ('msan','memory')
51  elif 'TSAN' in extra_tokens:
52    san = ('tsan','thread')
53
54  if san:
55    short,full = san
56    clang_linux = str(api.vars.workdir.join('clang_linux'))
57    libcxx = clang_linux + '/' + short
58    cflags = ' '.join([
59      '-fsanitize=' + full,
60      '-stdlib=libc++',
61      '-L%s/lib' % libcxx,
62      '-lc++abi',
63      '-I%s/include' % libcxx,
64      '-I%s/include/c++/v1' % libcxx,
65    ])
66    swiftshader_opts.extend([
67      '-DSWIFTSHADER_{}=ON'.format(short.upper()),
68      '-DCMAKE_C_FLAGS=%s' % cflags,
69      '-DCMAKE_CXX_FLAGS=%s' % cflags,
70    ])
71
72  # Build SwiftShader.
73  api.file.ensure_directory('makedirs swiftshader_out', out)
74  with api.context(cwd=out, env=env):
75    api.run(api.step, 'swiftshader cmake',
76            cmd=['cmake'] + swiftshader_opts + [swiftshader_root, '-GNinja'])
77    # See https://swiftshader-review.googlesource.com/c/SwiftShader/+/56452 for when the
78    # deprecated targets were added. See skbug.com/12386 for longer-term plans.
79    api.run(api.step, 'swiftshader ninja',
80            cmd=['ninja', '-C', out, 'libEGL_deprecated.so', 'libGLESv2_deprecated.so'])
81    api.run(api.step, 'rename legacy libEGL binary',
82            cmd=['cp', 'libEGL_deprecated.so', 'libEGL.so'])
83    api.run(api.step, 'rename legacy libGLESv2 binary',
84            cmd=['cp', 'libGLESv2_deprecated.so', 'libGLESv2.so'])
85
86
87def compile_fn(api, checkout_root, out_dir):
88  skia_dir      = checkout_root.join('skia')
89  compiler      = api.vars.builder_cfg.get('compiler',      '')
90  configuration = api.vars.builder_cfg.get('configuration', '')
91  extra_tokens  = api.vars.extra_tokens
92  os            = api.vars.builder_cfg.get('os',            '')
93  target_arch   = api.vars.builder_cfg.get('target_arch',   '')
94
95  clang_linux      = str(api.vars.workdir.join('clang_linux'))
96  win_toolchain    = str(api.vars.workdir.join('win_toolchain'))
97
98  cc, cxx, ccache = None, None, None
99  extra_cflags = []
100  extra_ldflags = []
101  args = {'werror': 'true'}
102  env = {}
103
104  if os == 'Mac':
105    # XCode build is listed in parentheses after the version at
106    # https://developer.apple.com/news/releases/, or on Wikipedia here:
107    # https://en.wikipedia.org/wiki/Xcode#Version_comparison_table
108    # Use lowercase letters.
109    # https://chrome-infra-packages.appspot.com/p/infra_internal/ios/xcode
110    XCODE_BUILD_VERSION = '12c33'
111    if compiler == 'Xcode11.4.1':
112      XCODE_BUILD_VERSION = '11e503a'
113    extra_cflags.append(
114        '-DREBUILD_IF_CHANGED_xcode_build_version=%s' % XCODE_BUILD_VERSION)
115    mac_toolchain_cmd = api.vars.workdir.join(
116        'mac_toolchain', 'mac_toolchain')
117    xcode_app_path = api.vars.cache_dir.join('Xcode.app')
118    # Copied from
119    # https://chromium.googlesource.com/chromium/tools/build/+/e19b7d9390e2bb438b566515b141ed2b9ed2c7c2/scripts/slave/recipe_modules/ios/api.py#322
120    with api.step.nest('ensure xcode') as step_result:
121      step_result.presentation.step_text = (
122          'Ensuring Xcode version %s in %s' % (
123              XCODE_BUILD_VERSION, xcode_app_path))
124      install_xcode_cmd = [
125          mac_toolchain_cmd, 'install',
126          # "ios" is needed for simulator builds
127          # (Build-Mac-Clang-x64-Release-iOS).
128          '-kind', 'ios',
129          '-xcode-version', XCODE_BUILD_VERSION,
130          '-output-dir', xcode_app_path,
131      ]
132      api.step('install xcode', install_xcode_cmd)
133      api.step('select xcode', [
134          'sudo', 'xcode-select', '-switch', xcode_app_path])
135      if 'iOS' in extra_tokens:
136        # Need to verify compilation for Metal on 9.0 and above
137        env['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0'
138        args['ios_min_target'] = '"9.0"'
139      else:
140        # We have some bots on 10.13.
141        env['MACOSX_DEPLOYMENT_TARGET'] = '10.13'
142
143  if 'CheckGeneratedFiles' in extra_tokens:
144    compiler = 'Clang'
145    args['skia_compile_processors'] = 'true'
146    args['skia_compile_sksl_tests'] = 'true'
147    args['skia_generate_workarounds'] = 'true'
148
149  # ccache + clang-tidy.sh chokes on the argument list.
150  if (api.vars.is_linux or os == 'Mac' or os == 'Mac10.15.5' or os == 'Mac10.15.7') and 'Tidy' not in extra_tokens:
151    if api.vars.is_linux:
152      ccache = api.vars.workdir.join('ccache_linux', 'bin', 'ccache')
153      # As of 2020-02-07, the sum of each Debian10-Clang-x86
154      # non-flutter/android/chromebook build takes less than 75G cache space.
155      env['CCACHE_MAXSIZE'] = '75G'
156    else:
157      ccache = api.vars.workdir.join('ccache_mac', 'bin', 'ccache')
158      # As of 2020-02-10, the sum of each Build-Mac-Clang- non-android build
159      # takes ~30G cache space.
160      env['CCACHE_MAXSIZE'] = '50G'
161
162    args['cc_wrapper'] = '"%s"' % ccache
163
164    env['CCACHE_DIR'] = api.vars.cache_dir.join('ccache')
165    env['CCACHE_MAXFILES'] = '0'
166    # Compilers are unpacked from cipd with bogus timestamps, only contribute
167    # compiler content to hashes. If Ninja ever uses absolute paths to changing
168    # directories we'll also need to set a CCACHE_BASEDIR.
169    env['CCACHE_COMPILERCHECK'] = 'content'
170
171  if compiler == 'Clang' and api.vars.is_linux:
172    cc  = clang_linux + '/bin/clang'
173    cxx = clang_linux + '/bin/clang++'
174    extra_cflags .append('-B%s/bin' % clang_linux)
175    extra_ldflags.append('-B%s/bin' % clang_linux)
176    extra_ldflags.append('-fuse-ld=lld')
177    extra_cflags.append('-DPLACEHOLDER_clang_linux_version=%s' %
178                        api.run.asset_version('clang_linux', skia_dir))
179    if 'Static' in extra_tokens:
180      extra_ldflags.extend(['-static-libstdc++', '-static-libgcc'])
181
182  elif compiler == 'Clang':
183    cc, cxx = 'clang', 'clang++'
184
185  if 'Tidy' in extra_tokens:
186    # Swap in clang-tidy.sh for clang++, but update PATH so it can find clang++.
187    cxx = skia_dir.join("tools/clang-tidy.sh")
188    env['PATH'] = '%s:%%(PATH)s' % (clang_linux + '/bin')
189    # Increase ClangTidy code coverage by enabling features.
190    args.update({
191      'skia_enable_fontmgr_empty':     'true',
192      'skia_enable_pdf':               'true',
193      'skia_use_expat':                'true',
194      'skia_use_freetype':             'true',
195      'skia_use_vulkan':               'true',
196    })
197
198  if 'Coverage' in extra_tokens:
199    # See https://clang.llvm.org/docs/SourceBasedCodeCoverage.html for
200    # more info on using llvm to gather coverage information.
201    extra_cflags.append('-fprofile-instr-generate')
202    extra_cflags.append('-fcoverage-mapping')
203    extra_ldflags.append('-fprofile-instr-generate')
204    extra_ldflags.append('-fcoverage-mapping')
205
206  if compiler != 'MSVC' and configuration == 'Debug':
207    extra_cflags.append('-O1')
208
209  if 'Exceptions' in extra_tokens:
210    extra_cflags.append('/EHsc')
211  if 'Fast' in extra_tokens:
212    extra_cflags.extend(['-march=native', '-fomit-frame-pointer', '-O3',
213                         '-ffp-contract=off'])
214
215  if len(extra_tokens) == 1 and extra_tokens[0].startswith('SK'):
216    extra_cflags.append('-D' + extra_tokens[0])
217    # If we're limiting Skia at all, drop skcms to portable code.
218    if 'SK_CPU_LIMIT' in extra_tokens[0]:
219      extra_cflags.append('-DSKCMS_PORTABLE')
220
221  if 'MSAN' in extra_tokens:
222    extra_ldflags.append('-L' + clang_linux + '/msan')
223  elif 'TSAN' in extra_tokens:
224    extra_ldflags.append('-L' + clang_linux + '/tsan')
225  elif api.vars.is_linux:
226    extra_ldflags.append('-L' + clang_linux + '/lib')
227
228  if configuration != 'Debug':
229    args['is_debug'] = 'false'
230  if 'Dawn' in extra_tokens:
231    args['skia_use_dawn'] = 'true'
232    args['skia_use_gl'] = 'false'
233    # Dawn imports jinja2, which imports markupsafe. Along with DEPS, make it
234    # importable.
235    env['PYTHONPATH'] = api.path.pathsep.join([
236        str(skia_dir.join('third_party', 'externals')), '%%(PYTHONPATH)s'])
237  if 'ANGLE' in extra_tokens:
238    args['skia_use_angle'] = 'true'
239  if 'SwiftShader' in extra_tokens:
240    swiftshader_root = skia_dir.join('third_party', 'externals', 'swiftshader')
241    swiftshader_out = out_dir.join('swiftshader_out')
242    compile_swiftshader(api, extra_tokens, swiftshader_root, cc, cxx, swiftshader_out)
243    args['skia_use_egl'] = 'true'
244    extra_cflags.extend([
245        '-DGR_EGL_TRY_GLES3_THEN_GLES2',
246        '-I%s' % skia_dir.join(
247            'third_party', 'externals', 'egl-registry', 'api'),
248        '-I%s' % skia_dir.join(
249            'third_party', 'externals', 'opengl-registry', 'api'),
250    ])
251    extra_ldflags.extend([
252        '-L%s' % swiftshader_out,
253    ])
254  if 'CommandBuffer' in extra_tokens:
255    # CommandBuffer runs against GLES version of CommandBuffer also, so
256    # include both.
257    args.update({
258      'skia_gl_standard': '""',
259    })
260    chrome_dir = checkout_root
261    api.run.run_once(build_command_buffer, api, chrome_dir, skia_dir, out_dir)
262  if 'MSAN' in extra_tokens:
263    args['skia_use_fontconfig'] = 'false'
264  if 'ASAN' in extra_tokens:
265    args['skia_enable_spirv_validation'] = 'false'
266  if 'Graphite' in extra_tokens:
267    args['skia_enable_graphite'] = 'true'
268    args['skia_use_metal'] = 'true'
269    if 'NoGpu' in extra_tokens:
270      args['skia_enable_gpu'] = 'false'
271  if 'NoDEPS' in extra_tokens:
272    args.update({
273      'is_official_build':             'true',
274      'skia_enable_fontmgr_empty':     'true',
275      'skia_enable_gpu':               'true',
276
277      'skia_enable_pdf':               'false',
278      'skia_use_expat':                'false',
279      'skia_use_freetype':             'false',
280      'skia_use_harfbuzz':             'false',
281      'skia_use_icu':                  'false',
282      'skia_use_libjpeg_turbo_decode': 'false',
283      'skia_use_libjpeg_turbo_encode': 'false',
284      'skia_use_libpng_decode':        'false',
285      'skia_use_libpng_encode':        'false',
286      'skia_use_libwebp_decode':       'false',
287      'skia_use_libwebp_encode':       'false',
288      'skia_use_vulkan':               'false',
289      'skia_use_zlib':                 'false',
290    })
291  if 'Shared' in extra_tokens:
292    args['is_component_build'] = 'true'
293  if 'Vulkan' in extra_tokens and not 'Android' in extra_tokens:
294    args['skia_use_vulkan'] = 'true'
295    args['skia_enable_vulkan_debug_layers'] = 'true'
296    args['skia_use_gl'] = 'false'
297  if 'Direct3D' in extra_tokens:
298    args['skia_use_direct3d'] = 'true'
299    args['skia_use_gl'] = 'false'
300  if 'Metal' in extra_tokens:
301    args['skia_use_metal'] = 'true'
302    args['skia_use_gl'] = 'false'
303  if 'iOS' in extra_tokens:
304    # Bots use Chromium signing cert.
305    args['skia_ios_identity'] = '".*GS9WA.*"'
306    # Get mobileprovision via the CIPD package.
307    args['skia_ios_profile'] = '"%s"' % api.vars.workdir.join(
308        'provisioning_profile_ios',
309        'Upstream_Testing_Provisioning_Profile.mobileprovision')
310  if compiler == 'Clang' and 'Win' in os:
311    args['clang_win'] = '"%s"' % api.vars.workdir.join('clang_win')
312    extra_cflags.append('-DPLACEHOLDER_clang_win_version=%s' %
313                        api.run.asset_version('clang_win', skia_dir))
314
315  sanitize = ''
316  for t in extra_tokens:
317    if t.endswith('SAN'):
318      sanitize = t
319      if api.vars.is_linux and t == 'ASAN':
320        # skia:8712 and skia:8713
321        extra_cflags.append('-DSK_ENABLE_SCOPED_LSAN_SUPPRESSIONS')
322  if 'SafeStack' in extra_tokens:
323    assert sanitize == ''
324    sanitize = 'safe-stack'
325
326  if 'Wuffs' in extra_tokens:
327    args['skia_use_wuffs'] = 'true'
328
329  for (k,v) in {
330    'cc':  cc,
331    'cxx': cxx,
332    'sanitize': sanitize,
333    'target_cpu': target_arch,
334    'target_os': 'ios' if 'iOS' in extra_tokens else '',
335    'win_sdk': win_toolchain + '/win_sdk' if 'Win' in os else '',
336    'win_vc': win_toolchain + '/VC' if 'Win' in os else '',
337  }.items():
338    if v:
339      args[k] = '"%s"' % v
340  if extra_cflags:
341    args['extra_cflags'] = repr(extra_cflags).replace("'", '"')
342  if extra_ldflags:
343    args['extra_ldflags'] = repr(extra_ldflags).replace("'", '"')
344
345  gn_args = ' '.join('%s=%s' % (k,v) for (k,v) in sorted(args.items()))
346  gn = skia_dir.join('bin', 'gn')
347
348  with api.context(cwd=skia_dir):
349    api.run(api.python,
350            'fetch-gn',
351            script=skia_dir.join('bin', 'fetch-gn'),
352            infra_step=True)
353    if 'CheckGeneratedFiles' in extra_tokens:
354      env['PATH'] = '%s:%%(PATH)s' % skia_dir.join('bin')
355      api.run(api.python,
356              'fetch-clang-format',
357              script=skia_dir.join('bin', 'fetch-clang-format'),
358              infra_step=True)
359
360    with api.env(env):
361      if ccache:
362        api.run(api.step, 'ccache stats-start', cmd=[ccache, '-s'])
363      api.run(api.step, 'gn gen',
364              cmd=[gn, 'gen', out_dir, '--args=' + gn_args])
365      api.run(api.step, 'ninja', cmd=['ninja', '-C', out_dir])
366      if ccache:
367        api.run(api.step, 'ccache stats-end', cmd=[ccache, '-s'])
368
369
370def copy_build_products(api, src, dst):
371  util.copy_listed_files(api, src, dst, util.DEFAULT_BUILD_PRODUCTS)
372  extra_tokens  = api.vars.extra_tokens
373  os            = api.vars.builder_cfg.get('os', '')
374
375  if 'SwiftShader' in extra_tokens:
376    util.copy_listed_files(api,
377        src.join('swiftshader_out'),
378        api.vars.swarming_out_dir.join('swiftshader_out'),
379        util.DEFAULT_BUILD_PRODUCTS)
380
381  if os == 'Mac' and any('SAN' in t for t in extra_tokens):
382    # The XSAN dylibs are in
383    # Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib
384    # /clang/11.0.0/lib/darwin, where 11.0.0 could change in future versions.
385    xcode_clang_ver_dirs = api.file.listdir(
386        'find XCode Clang version',
387        api.vars.cache_dir.join(
388            'Xcode.app', 'Contents', 'Developer', 'Toolchains',
389            'XcodeDefault.xctoolchain', 'usr', 'lib', 'clang'),
390        test_data=['11.0.0'])
391    assert len(xcode_clang_ver_dirs) == 1
392    dylib_dir = xcode_clang_ver_dirs[0].join('lib', 'darwin')
393    dylibs = api.file.glob_paths('find xSAN dylibs', dylib_dir,
394                                 'libclang_rt.*san_osx_dynamic.dylib',
395                                 test_data=[
396                                     'libclang_rt.asan_osx_dynamic.dylib',
397                                     'libclang_rt.tsan_osx_dynamic.dylib',
398                                     'libclang_rt.ubsan_osx_dynamic.dylib',
399                                 ])
400    for f in dylibs:
401      api.file.copy('copy %s' % api.path.basename(f), f, dst)
402