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# pylint: disable=W0201 7 8 9from recipe_engine import recipe_api 10 11 12BUILD_PRODUCTS_ISOLATE_WHITELIST = [ 13 'dm', 14 'dm.exe', 15 'get_images_from_skps', 16 'get_images_from_skps.exe', 17 'nanobench', 18 'nanobench.exe', 19 'skpbench', 20 '*.so', 21 '*.dll', 22 '*.dylib', 23 'skia_launcher', 24 'lib/*.so', 25 'iOSShell.app', 26 'iOSShell.ipa', 27 'visualbench', 28 'visualbench.exe', 29 'vulkan-1.dll', 30] 31 32 33class SkiaStepApi(recipe_api.RecipeApi): 34 35 def __init__(self, *args, **kwargs): 36 """Initialize the recipe module.""" 37 super(SkiaStepApi, self).__init__(*args, **kwargs) 38 39 self._already_ran = {} 40 self._ccache = None 41 self._checked_for_ccache = False 42 self._failed = [] 43 44 def check_failure(self): 45 """Raise an exception if any step failed.""" 46 if self._failed: 47 raise self.m.step.StepFailure('Failed build steps: %s' % 48 ', '.join([f.name for f in self._failed])) 49 50 @property 51 def failed_steps(self): 52 return self._failed[:] 53 54 def run_once(self, fn, *args, **kwargs): 55 if not fn.__name__ in self._already_ran: 56 self._already_ran[fn.__name__] = fn(*args, **kwargs) 57 return self._already_ran[fn.__name__] 58 59 def readfile(self, filename, *args, **kwargs): 60 """Convenience function for reading files.""" 61 name = kwargs.pop('name') or 'read %s' % self.m.path.basename(filename) 62 return self.m.file.read(name, filename, infra_step=True, *args, **kwargs) 63 64 def writefile(self, filename, contents): 65 """Convenience function for writing files.""" 66 return self.m.file.write('write %s' % self.m.path.basename(filename), 67 filename, contents, infra_step=True) 68 69 def rmtree(self, path): 70 """Wrapper around api.file.rmtree with environment fix.""" 71 env = self.m.step.get_from_context('env', {}) 72 env['PYTHONPATH'] = str(self.m.path['start_dir'].join( 73 'skia', 'infra', 'bots', '.recipe_deps', 'build', 'scripts')) 74 with self.m.step.context({'env': env}): 75 self.m.file.rmtree(self.m.path.basename(path), 76 path, 77 infra_step=True) 78 79 def __call__(self, steptype, name, abort_on_failure=True, 80 fail_build_on_failure=True, **kwargs): 81 """Run a step. If it fails, keep going but mark the build status failed.""" 82 env = self.m.step.get_from_context('env', {}) 83 env.update(self.m.vars.default_env) 84 try: 85 with self.m.step.context({'env': env}): 86 return steptype(name=name, **kwargs) 87 except self.m.step.StepFailure as e: 88 if abort_on_failure or fail_build_on_failure: 89 self._failed.append(e) 90 if abort_on_failure: 91 raise # pragma: no cover 92 93 def copy_build_products(self, src, dst): 94 """Copy whitelisted build products from src to dst.""" 95 self.m.python.inline( 96 name='copy build products', 97 program='''import errno 98import glob 99import os 100import shutil 101import sys 102 103src = sys.argv[1] 104dst = sys.argv[2] 105build_products_whitelist = %s 106 107try: 108 os.makedirs(dst) 109except OSError as e: 110 if e.errno != errno.EEXIST: 111 raise 112 113for pattern in build_products_whitelist: 114 path = os.path.join(src, pattern) 115 for f in glob.glob(path): 116 dst_path = os.path.join(dst, os.path.relpath(f, src)) 117 if not os.path.isdir(os.path.dirname(dst_path)): 118 os.makedirs(os.path.dirname(dst_path)) 119 print 'Copying build product %%s to %%s' %% (f, dst_path) 120 shutil.move(f, dst_path) 121''' % str(BUILD_PRODUCTS_ISOLATE_WHITELIST), 122 args=[src, dst], 123 infra_step=True) 124 125 def with_retry(self, steptype, name, attempts, **kwargs): 126 for attempt in xrange(attempts): 127 step_name = name 128 if attempt > 0: 129 step_name += ' (attempt %d)' % (attempt + 1) 130 try: 131 res = self(steptype, name=step_name, **kwargs) 132 return res 133 except self.m.step.StepFailure: 134 if attempt == attempts - 1: 135 raise 136