1*4882a593Smuzhiyun# Copyright (c) 2014 Google, Inc 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0+ 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun 6*4882a593Smuzhiyunimport errno 7*4882a593Smuzhiyunimport glob 8*4882a593Smuzhiyunimport os 9*4882a593Smuzhiyunimport shutil 10*4882a593Smuzhiyunimport threading 11*4882a593Smuzhiyun 12*4882a593Smuzhiyunimport command 13*4882a593Smuzhiyunimport gitutil 14*4882a593Smuzhiyun 15*4882a593SmuzhiyunRETURN_CODE_RETRY = -1 16*4882a593Smuzhiyun 17*4882a593Smuzhiyundef Mkdir(dirname, parents = False): 18*4882a593Smuzhiyun """Make a directory if it doesn't already exist. 19*4882a593Smuzhiyun 20*4882a593Smuzhiyun Args: 21*4882a593Smuzhiyun dirname: Directory to create 22*4882a593Smuzhiyun """ 23*4882a593Smuzhiyun try: 24*4882a593Smuzhiyun if parents: 25*4882a593Smuzhiyun os.makedirs(dirname) 26*4882a593Smuzhiyun else: 27*4882a593Smuzhiyun os.mkdir(dirname) 28*4882a593Smuzhiyun except OSError as err: 29*4882a593Smuzhiyun if err.errno == errno.EEXIST: 30*4882a593Smuzhiyun pass 31*4882a593Smuzhiyun else: 32*4882a593Smuzhiyun raise 33*4882a593Smuzhiyun 34*4882a593Smuzhiyunclass BuilderJob: 35*4882a593Smuzhiyun """Holds information about a job to be performed by a thread 36*4882a593Smuzhiyun 37*4882a593Smuzhiyun Members: 38*4882a593Smuzhiyun board: Board object to build 39*4882a593Smuzhiyun commits: List of commit options to build. 40*4882a593Smuzhiyun """ 41*4882a593Smuzhiyun def __init__(self): 42*4882a593Smuzhiyun self.board = None 43*4882a593Smuzhiyun self.commits = [] 44*4882a593Smuzhiyun 45*4882a593Smuzhiyun 46*4882a593Smuzhiyunclass ResultThread(threading.Thread): 47*4882a593Smuzhiyun """This thread processes results from builder threads. 48*4882a593Smuzhiyun 49*4882a593Smuzhiyun It simply passes the results on to the builder. There is only one 50*4882a593Smuzhiyun result thread, and this helps to serialise the build output. 51*4882a593Smuzhiyun """ 52*4882a593Smuzhiyun def __init__(self, builder): 53*4882a593Smuzhiyun """Set up a new result thread 54*4882a593Smuzhiyun 55*4882a593Smuzhiyun Args: 56*4882a593Smuzhiyun builder: Builder which will be sent each result 57*4882a593Smuzhiyun """ 58*4882a593Smuzhiyun threading.Thread.__init__(self) 59*4882a593Smuzhiyun self.builder = builder 60*4882a593Smuzhiyun 61*4882a593Smuzhiyun def run(self): 62*4882a593Smuzhiyun """Called to start up the result thread. 63*4882a593Smuzhiyun 64*4882a593Smuzhiyun We collect the next result job and pass it on to the build. 65*4882a593Smuzhiyun """ 66*4882a593Smuzhiyun while True: 67*4882a593Smuzhiyun result = self.builder.out_queue.get() 68*4882a593Smuzhiyun self.builder.ProcessResult(result) 69*4882a593Smuzhiyun self.builder.out_queue.task_done() 70*4882a593Smuzhiyun 71*4882a593Smuzhiyun 72*4882a593Smuzhiyunclass BuilderThread(threading.Thread): 73*4882a593Smuzhiyun """This thread builds U-Boot for a particular board. 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun An input queue provides each new job. We run 'make' to build U-Boot 76*4882a593Smuzhiyun and then pass the results on to the output queue. 77*4882a593Smuzhiyun 78*4882a593Smuzhiyun Members: 79*4882a593Smuzhiyun builder: The builder which contains information we might need 80*4882a593Smuzhiyun thread_num: Our thread number (0-n-1), used to decide on a 81*4882a593Smuzhiyun temporary directory 82*4882a593Smuzhiyun """ 83*4882a593Smuzhiyun def __init__(self, builder, thread_num, incremental, per_board_out_dir): 84*4882a593Smuzhiyun """Set up a new builder thread""" 85*4882a593Smuzhiyun threading.Thread.__init__(self) 86*4882a593Smuzhiyun self.builder = builder 87*4882a593Smuzhiyun self.thread_num = thread_num 88*4882a593Smuzhiyun self.incremental = incremental 89*4882a593Smuzhiyun self.per_board_out_dir = per_board_out_dir 90*4882a593Smuzhiyun 91*4882a593Smuzhiyun def Make(self, commit, brd, stage, cwd, *args, **kwargs): 92*4882a593Smuzhiyun """Run 'make' on a particular commit and board. 93*4882a593Smuzhiyun 94*4882a593Smuzhiyun The source code will already be checked out, so the 'commit' 95*4882a593Smuzhiyun argument is only for information. 96*4882a593Smuzhiyun 97*4882a593Smuzhiyun Args: 98*4882a593Smuzhiyun commit: Commit object that is being built 99*4882a593Smuzhiyun brd: Board object that is being built 100*4882a593Smuzhiyun stage: Stage of the build. Valid stages are: 101*4882a593Smuzhiyun mrproper - can be called to clean source 102*4882a593Smuzhiyun config - called to configure for a board 103*4882a593Smuzhiyun build - the main make invocation - it does the build 104*4882a593Smuzhiyun args: A list of arguments to pass to 'make' 105*4882a593Smuzhiyun kwargs: A list of keyword arguments to pass to command.RunPipe() 106*4882a593Smuzhiyun 107*4882a593Smuzhiyun Returns: 108*4882a593Smuzhiyun CommandResult object 109*4882a593Smuzhiyun """ 110*4882a593Smuzhiyun return self.builder.do_make(commit, brd, stage, cwd, *args, 111*4882a593Smuzhiyun **kwargs) 112*4882a593Smuzhiyun 113*4882a593Smuzhiyun def RunCommit(self, commit_upto, brd, work_dir, do_config, config_only, 114*4882a593Smuzhiyun force_build, force_build_failures): 115*4882a593Smuzhiyun """Build a particular commit. 116*4882a593Smuzhiyun 117*4882a593Smuzhiyun If the build is already done, and we are not forcing a build, we skip 118*4882a593Smuzhiyun the build and just return the previously-saved results. 119*4882a593Smuzhiyun 120*4882a593Smuzhiyun Args: 121*4882a593Smuzhiyun commit_upto: Commit number to build (0...n-1) 122*4882a593Smuzhiyun brd: Board object to build 123*4882a593Smuzhiyun work_dir: Directory to which the source will be checked out 124*4882a593Smuzhiyun do_config: True to run a make <board>_defconfig on the source 125*4882a593Smuzhiyun config_only: Only configure the source, do not build it 126*4882a593Smuzhiyun force_build: Force a build even if one was previously done 127*4882a593Smuzhiyun force_build_failures: Force a bulid if the previous result showed 128*4882a593Smuzhiyun failure 129*4882a593Smuzhiyun 130*4882a593Smuzhiyun Returns: 131*4882a593Smuzhiyun tuple containing: 132*4882a593Smuzhiyun - CommandResult object containing the results of the build 133*4882a593Smuzhiyun - boolean indicating whether 'make config' is still needed 134*4882a593Smuzhiyun """ 135*4882a593Smuzhiyun # Create a default result - it will be overwritte by the call to 136*4882a593Smuzhiyun # self.Make() below, in the event that we do a build. 137*4882a593Smuzhiyun result = command.CommandResult() 138*4882a593Smuzhiyun result.return_code = 0 139*4882a593Smuzhiyun if self.builder.in_tree: 140*4882a593Smuzhiyun out_dir = work_dir 141*4882a593Smuzhiyun else: 142*4882a593Smuzhiyun if self.per_board_out_dir: 143*4882a593Smuzhiyun out_rel_dir = os.path.join('..', brd.target) 144*4882a593Smuzhiyun else: 145*4882a593Smuzhiyun out_rel_dir = 'build' 146*4882a593Smuzhiyun out_dir = os.path.join(work_dir, out_rel_dir) 147*4882a593Smuzhiyun 148*4882a593Smuzhiyun # Check if the job was already completed last time 149*4882a593Smuzhiyun done_file = self.builder.GetDoneFile(commit_upto, brd.target) 150*4882a593Smuzhiyun result.already_done = os.path.exists(done_file) 151*4882a593Smuzhiyun will_build = (force_build or force_build_failures or 152*4882a593Smuzhiyun not result.already_done) 153*4882a593Smuzhiyun if result.already_done: 154*4882a593Smuzhiyun # Get the return code from that build and use it 155*4882a593Smuzhiyun with open(done_file, 'r') as fd: 156*4882a593Smuzhiyun result.return_code = int(fd.readline()) 157*4882a593Smuzhiyun 158*4882a593Smuzhiyun # Check the signal that the build needs to be retried 159*4882a593Smuzhiyun if result.return_code == RETURN_CODE_RETRY: 160*4882a593Smuzhiyun will_build = True 161*4882a593Smuzhiyun elif will_build: 162*4882a593Smuzhiyun err_file = self.builder.GetErrFile(commit_upto, brd.target) 163*4882a593Smuzhiyun if os.path.exists(err_file) and os.stat(err_file).st_size: 164*4882a593Smuzhiyun result.stderr = 'bad' 165*4882a593Smuzhiyun elif not force_build: 166*4882a593Smuzhiyun # The build passed, so no need to build it again 167*4882a593Smuzhiyun will_build = False 168*4882a593Smuzhiyun 169*4882a593Smuzhiyun if will_build: 170*4882a593Smuzhiyun # We are going to have to build it. First, get a toolchain 171*4882a593Smuzhiyun if not self.toolchain: 172*4882a593Smuzhiyun try: 173*4882a593Smuzhiyun self.toolchain = self.builder.toolchains.Select(brd.arch) 174*4882a593Smuzhiyun except ValueError as err: 175*4882a593Smuzhiyun result.return_code = 10 176*4882a593Smuzhiyun result.stdout = '' 177*4882a593Smuzhiyun result.stderr = str(err) 178*4882a593Smuzhiyun # TODO(sjg@chromium.org): This gets swallowed, but needs 179*4882a593Smuzhiyun # to be reported. 180*4882a593Smuzhiyun 181*4882a593Smuzhiyun if self.toolchain: 182*4882a593Smuzhiyun # Checkout the right commit 183*4882a593Smuzhiyun if self.builder.commits: 184*4882a593Smuzhiyun commit = self.builder.commits[commit_upto] 185*4882a593Smuzhiyun if self.builder.checkout: 186*4882a593Smuzhiyun git_dir = os.path.join(work_dir, '.git') 187*4882a593Smuzhiyun gitutil.Checkout(commit.hash, git_dir, work_dir, 188*4882a593Smuzhiyun force=True) 189*4882a593Smuzhiyun else: 190*4882a593Smuzhiyun commit = 'current' 191*4882a593Smuzhiyun 192*4882a593Smuzhiyun # Set up the environment and command line 193*4882a593Smuzhiyun env = self.toolchain.MakeEnvironment(self.builder.full_path) 194*4882a593Smuzhiyun Mkdir(out_dir) 195*4882a593Smuzhiyun args = [] 196*4882a593Smuzhiyun cwd = work_dir 197*4882a593Smuzhiyun src_dir = os.path.realpath(work_dir) 198*4882a593Smuzhiyun if not self.builder.in_tree: 199*4882a593Smuzhiyun if commit_upto is None: 200*4882a593Smuzhiyun # In this case we are building in the original source 201*4882a593Smuzhiyun # directory (i.e. the current directory where buildman 202*4882a593Smuzhiyun # is invoked. The output directory is set to this 203*4882a593Smuzhiyun # thread's selected work directory. 204*4882a593Smuzhiyun # 205*4882a593Smuzhiyun # Symlinks can confuse U-Boot's Makefile since 206*4882a593Smuzhiyun # we may use '..' in our path, so remove them. 207*4882a593Smuzhiyun out_dir = os.path.realpath(out_dir) 208*4882a593Smuzhiyun args.append('O=%s' % out_dir) 209*4882a593Smuzhiyun cwd = None 210*4882a593Smuzhiyun src_dir = os.getcwd() 211*4882a593Smuzhiyun else: 212*4882a593Smuzhiyun args.append('O=%s' % out_rel_dir) 213*4882a593Smuzhiyun if self.builder.verbose_build: 214*4882a593Smuzhiyun args.append('V=1') 215*4882a593Smuzhiyun else: 216*4882a593Smuzhiyun args.append('-s') 217*4882a593Smuzhiyun if self.builder.num_jobs is not None: 218*4882a593Smuzhiyun args.extend(['-j', str(self.builder.num_jobs)]) 219*4882a593Smuzhiyun config_args = ['%s_defconfig' % brd.target] 220*4882a593Smuzhiyun config_out = '' 221*4882a593Smuzhiyun args.extend(self.builder.toolchains.GetMakeArguments(brd)) 222*4882a593Smuzhiyun 223*4882a593Smuzhiyun # If we need to reconfigure, do that now 224*4882a593Smuzhiyun if do_config: 225*4882a593Smuzhiyun config_out = '' 226*4882a593Smuzhiyun if not self.incremental: 227*4882a593Smuzhiyun result = self.Make(commit, brd, 'mrproper', cwd, 228*4882a593Smuzhiyun 'mrproper', *args, env=env) 229*4882a593Smuzhiyun config_out += result.combined 230*4882a593Smuzhiyun result = self.Make(commit, brd, 'config', cwd, 231*4882a593Smuzhiyun *(args + config_args), env=env) 232*4882a593Smuzhiyun config_out += result.combined 233*4882a593Smuzhiyun do_config = False # No need to configure next time 234*4882a593Smuzhiyun if result.return_code == 0: 235*4882a593Smuzhiyun if config_only: 236*4882a593Smuzhiyun args.append('cfg') 237*4882a593Smuzhiyun result = self.Make(commit, brd, 'build', cwd, *args, 238*4882a593Smuzhiyun env=env) 239*4882a593Smuzhiyun result.stderr = result.stderr.replace(src_dir + '/', '') 240*4882a593Smuzhiyun if self.builder.verbose_build: 241*4882a593Smuzhiyun result.stdout = config_out + result.stdout 242*4882a593Smuzhiyun else: 243*4882a593Smuzhiyun result.return_code = 1 244*4882a593Smuzhiyun result.stderr = 'No tool chain for %s\n' % brd.arch 245*4882a593Smuzhiyun result.already_done = False 246*4882a593Smuzhiyun 247*4882a593Smuzhiyun result.toolchain = self.toolchain 248*4882a593Smuzhiyun result.brd = brd 249*4882a593Smuzhiyun result.commit_upto = commit_upto 250*4882a593Smuzhiyun result.out_dir = out_dir 251*4882a593Smuzhiyun return result, do_config 252*4882a593Smuzhiyun 253*4882a593Smuzhiyun def _WriteResult(self, result, keep_outputs): 254*4882a593Smuzhiyun """Write a built result to the output directory. 255*4882a593Smuzhiyun 256*4882a593Smuzhiyun Args: 257*4882a593Smuzhiyun result: CommandResult object containing result to write 258*4882a593Smuzhiyun keep_outputs: True to store the output binaries, False 259*4882a593Smuzhiyun to delete them 260*4882a593Smuzhiyun """ 261*4882a593Smuzhiyun # Fatal error 262*4882a593Smuzhiyun if result.return_code < 0: 263*4882a593Smuzhiyun return 264*4882a593Smuzhiyun 265*4882a593Smuzhiyun # If we think this might have been aborted with Ctrl-C, record the 266*4882a593Smuzhiyun # failure but not that we are 'done' with this board. A retry may fix 267*4882a593Smuzhiyun # it. 268*4882a593Smuzhiyun maybe_aborted = result.stderr and 'No child processes' in result.stderr 269*4882a593Smuzhiyun 270*4882a593Smuzhiyun if result.already_done: 271*4882a593Smuzhiyun return 272*4882a593Smuzhiyun 273*4882a593Smuzhiyun # Write the output and stderr 274*4882a593Smuzhiyun output_dir = self.builder._GetOutputDir(result.commit_upto) 275*4882a593Smuzhiyun Mkdir(output_dir) 276*4882a593Smuzhiyun build_dir = self.builder.GetBuildDir(result.commit_upto, 277*4882a593Smuzhiyun result.brd.target) 278*4882a593Smuzhiyun Mkdir(build_dir) 279*4882a593Smuzhiyun 280*4882a593Smuzhiyun outfile = os.path.join(build_dir, 'log') 281*4882a593Smuzhiyun with open(outfile, 'w') as fd: 282*4882a593Smuzhiyun if result.stdout: 283*4882a593Smuzhiyun # We don't want unicode characters in log files 284*4882a593Smuzhiyun fd.write(result.stdout.decode('UTF-8').encode('ASCII', 'replace')) 285*4882a593Smuzhiyun 286*4882a593Smuzhiyun errfile = self.builder.GetErrFile(result.commit_upto, 287*4882a593Smuzhiyun result.brd.target) 288*4882a593Smuzhiyun if result.stderr: 289*4882a593Smuzhiyun with open(errfile, 'w') as fd: 290*4882a593Smuzhiyun # We don't want unicode characters in log files 291*4882a593Smuzhiyun fd.write(result.stderr.decode('UTF-8').encode('ASCII', 'replace')) 292*4882a593Smuzhiyun elif os.path.exists(errfile): 293*4882a593Smuzhiyun os.remove(errfile) 294*4882a593Smuzhiyun 295*4882a593Smuzhiyun if result.toolchain: 296*4882a593Smuzhiyun # Write the build result and toolchain information. 297*4882a593Smuzhiyun done_file = self.builder.GetDoneFile(result.commit_upto, 298*4882a593Smuzhiyun result.brd.target) 299*4882a593Smuzhiyun with open(done_file, 'w') as fd: 300*4882a593Smuzhiyun if maybe_aborted: 301*4882a593Smuzhiyun # Special code to indicate we need to retry 302*4882a593Smuzhiyun fd.write('%s' % RETURN_CODE_RETRY) 303*4882a593Smuzhiyun else: 304*4882a593Smuzhiyun fd.write('%s' % result.return_code) 305*4882a593Smuzhiyun with open(os.path.join(build_dir, 'toolchain'), 'w') as fd: 306*4882a593Smuzhiyun print >>fd, 'gcc', result.toolchain.gcc 307*4882a593Smuzhiyun print >>fd, 'path', result.toolchain.path 308*4882a593Smuzhiyun print >>fd, 'cross', result.toolchain.cross 309*4882a593Smuzhiyun print >>fd, 'arch', result.toolchain.arch 310*4882a593Smuzhiyun fd.write('%s' % result.return_code) 311*4882a593Smuzhiyun 312*4882a593Smuzhiyun # Write out the image and function size information and an objdump 313*4882a593Smuzhiyun env = result.toolchain.MakeEnvironment(self.builder.full_path) 314*4882a593Smuzhiyun lines = [] 315*4882a593Smuzhiyun for fname in ['u-boot', 'spl/u-boot-spl']: 316*4882a593Smuzhiyun cmd = ['%snm' % self.toolchain.cross, '--size-sort', fname] 317*4882a593Smuzhiyun nm_result = command.RunPipe([cmd], capture=True, 318*4882a593Smuzhiyun capture_stderr=True, cwd=result.out_dir, 319*4882a593Smuzhiyun raise_on_error=False, env=env) 320*4882a593Smuzhiyun if nm_result.stdout: 321*4882a593Smuzhiyun nm = self.builder.GetFuncSizesFile(result.commit_upto, 322*4882a593Smuzhiyun result.brd.target, fname) 323*4882a593Smuzhiyun with open(nm, 'w') as fd: 324*4882a593Smuzhiyun print >>fd, nm_result.stdout, 325*4882a593Smuzhiyun 326*4882a593Smuzhiyun cmd = ['%sobjdump' % self.toolchain.cross, '-h', fname] 327*4882a593Smuzhiyun dump_result = command.RunPipe([cmd], capture=True, 328*4882a593Smuzhiyun capture_stderr=True, cwd=result.out_dir, 329*4882a593Smuzhiyun raise_on_error=False, env=env) 330*4882a593Smuzhiyun rodata_size = '' 331*4882a593Smuzhiyun if dump_result.stdout: 332*4882a593Smuzhiyun objdump = self.builder.GetObjdumpFile(result.commit_upto, 333*4882a593Smuzhiyun result.brd.target, fname) 334*4882a593Smuzhiyun with open(objdump, 'w') as fd: 335*4882a593Smuzhiyun print >>fd, dump_result.stdout, 336*4882a593Smuzhiyun for line in dump_result.stdout.splitlines(): 337*4882a593Smuzhiyun fields = line.split() 338*4882a593Smuzhiyun if len(fields) > 5 and fields[1] == '.rodata': 339*4882a593Smuzhiyun rodata_size = fields[2] 340*4882a593Smuzhiyun 341*4882a593Smuzhiyun cmd = ['%ssize' % self.toolchain.cross, fname] 342*4882a593Smuzhiyun size_result = command.RunPipe([cmd], capture=True, 343*4882a593Smuzhiyun capture_stderr=True, cwd=result.out_dir, 344*4882a593Smuzhiyun raise_on_error=False, env=env) 345*4882a593Smuzhiyun if size_result.stdout: 346*4882a593Smuzhiyun lines.append(size_result.stdout.splitlines()[1] + ' ' + 347*4882a593Smuzhiyun rodata_size) 348*4882a593Smuzhiyun 349*4882a593Smuzhiyun # Write out the image sizes file. This is similar to the output 350*4882a593Smuzhiyun # of binutil's 'size' utility, but it omits the header line and 351*4882a593Smuzhiyun # adds an additional hex value at the end of each line for the 352*4882a593Smuzhiyun # rodata size 353*4882a593Smuzhiyun if len(lines): 354*4882a593Smuzhiyun sizes = self.builder.GetSizesFile(result.commit_upto, 355*4882a593Smuzhiyun result.brd.target) 356*4882a593Smuzhiyun with open(sizes, 'w') as fd: 357*4882a593Smuzhiyun print >>fd, '\n'.join(lines) 358*4882a593Smuzhiyun 359*4882a593Smuzhiyun # Write out the configuration files, with a special case for SPL 360*4882a593Smuzhiyun for dirname in ['', 'spl', 'tpl']: 361*4882a593Smuzhiyun self.CopyFiles(result.out_dir, build_dir, dirname, ['u-boot.cfg', 362*4882a593Smuzhiyun 'spl/u-boot-spl.cfg', 'tpl/u-boot-tpl.cfg', '.config', 363*4882a593Smuzhiyun 'include/autoconf.mk', 'include/generated/autoconf.h']) 364*4882a593Smuzhiyun 365*4882a593Smuzhiyun # Now write the actual build output 366*4882a593Smuzhiyun if keep_outputs: 367*4882a593Smuzhiyun self.CopyFiles(result.out_dir, build_dir, '', ['u-boot*', '*.bin', 368*4882a593Smuzhiyun '*.map', '*.img', 'MLO', 'SPL', 'include/autoconf.mk', 369*4882a593Smuzhiyun 'spl/u-boot-spl*']) 370*4882a593Smuzhiyun 371*4882a593Smuzhiyun def CopyFiles(self, out_dir, build_dir, dirname, patterns): 372*4882a593Smuzhiyun """Copy files from the build directory to the output. 373*4882a593Smuzhiyun 374*4882a593Smuzhiyun Args: 375*4882a593Smuzhiyun out_dir: Path to output directory containing the files 376*4882a593Smuzhiyun build_dir: Place to copy the files 377*4882a593Smuzhiyun dirname: Source directory, '' for normal U-Boot, 'spl' for SPL 378*4882a593Smuzhiyun patterns: A list of filenames (strings) to copy, each relative 379*4882a593Smuzhiyun to the build directory 380*4882a593Smuzhiyun """ 381*4882a593Smuzhiyun for pattern in patterns: 382*4882a593Smuzhiyun file_list = glob.glob(os.path.join(out_dir, dirname, pattern)) 383*4882a593Smuzhiyun for fname in file_list: 384*4882a593Smuzhiyun target = os.path.basename(fname) 385*4882a593Smuzhiyun if dirname: 386*4882a593Smuzhiyun base, ext = os.path.splitext(target) 387*4882a593Smuzhiyun if ext: 388*4882a593Smuzhiyun target = '%s-%s%s' % (base, dirname, ext) 389*4882a593Smuzhiyun shutil.copy(fname, os.path.join(build_dir, target)) 390*4882a593Smuzhiyun 391*4882a593Smuzhiyun def RunJob(self, job): 392*4882a593Smuzhiyun """Run a single job 393*4882a593Smuzhiyun 394*4882a593Smuzhiyun A job consists of a building a list of commits for a particular board. 395*4882a593Smuzhiyun 396*4882a593Smuzhiyun Args: 397*4882a593Smuzhiyun job: Job to build 398*4882a593Smuzhiyun """ 399*4882a593Smuzhiyun brd = job.board 400*4882a593Smuzhiyun work_dir = self.builder.GetThreadDir(self.thread_num) 401*4882a593Smuzhiyun self.toolchain = None 402*4882a593Smuzhiyun if job.commits: 403*4882a593Smuzhiyun # Run 'make board_defconfig' on the first commit 404*4882a593Smuzhiyun do_config = True 405*4882a593Smuzhiyun commit_upto = 0 406*4882a593Smuzhiyun force_build = False 407*4882a593Smuzhiyun for commit_upto in range(0, len(job.commits), job.step): 408*4882a593Smuzhiyun result, request_config = self.RunCommit(commit_upto, brd, 409*4882a593Smuzhiyun work_dir, do_config, self.builder.config_only, 410*4882a593Smuzhiyun force_build or self.builder.force_build, 411*4882a593Smuzhiyun self.builder.force_build_failures) 412*4882a593Smuzhiyun failed = result.return_code or result.stderr 413*4882a593Smuzhiyun did_config = do_config 414*4882a593Smuzhiyun if failed and not do_config: 415*4882a593Smuzhiyun # If our incremental build failed, try building again 416*4882a593Smuzhiyun # with a reconfig. 417*4882a593Smuzhiyun if self.builder.force_config_on_failure: 418*4882a593Smuzhiyun result, request_config = self.RunCommit(commit_upto, 419*4882a593Smuzhiyun brd, work_dir, True, False, True, False) 420*4882a593Smuzhiyun did_config = True 421*4882a593Smuzhiyun if not self.builder.force_reconfig: 422*4882a593Smuzhiyun do_config = request_config 423*4882a593Smuzhiyun 424*4882a593Smuzhiyun # If we built that commit, then config is done. But if we got 425*4882a593Smuzhiyun # an warning, reconfig next time to force it to build the same 426*4882a593Smuzhiyun # files that created warnings this time. Otherwise an 427*4882a593Smuzhiyun # incremental build may not build the same file, and we will 428*4882a593Smuzhiyun # think that the warning has gone away. 429*4882a593Smuzhiyun # We could avoid this by using -Werror everywhere... 430*4882a593Smuzhiyun # For errors, the problem doesn't happen, since presumably 431*4882a593Smuzhiyun # the build stopped and didn't generate output, so will retry 432*4882a593Smuzhiyun # that file next time. So we could detect warnings and deal 433*4882a593Smuzhiyun # with them specially here. For now, we just reconfigure if 434*4882a593Smuzhiyun # anything goes work. 435*4882a593Smuzhiyun # Of course this is substantially slower if there are build 436*4882a593Smuzhiyun # errors/warnings (e.g. 2-3x slower even if only 10% of builds 437*4882a593Smuzhiyun # have problems). 438*4882a593Smuzhiyun if (failed and not result.already_done and not did_config and 439*4882a593Smuzhiyun self.builder.force_config_on_failure): 440*4882a593Smuzhiyun # If this build failed, try the next one with a 441*4882a593Smuzhiyun # reconfigure. 442*4882a593Smuzhiyun # Sometimes if the board_config.h file changes it can mess 443*4882a593Smuzhiyun # with dependencies, and we get: 444*4882a593Smuzhiyun # make: *** No rule to make target `include/autoconf.mk', 445*4882a593Smuzhiyun # needed by `depend'. 446*4882a593Smuzhiyun do_config = True 447*4882a593Smuzhiyun force_build = True 448*4882a593Smuzhiyun else: 449*4882a593Smuzhiyun force_build = False 450*4882a593Smuzhiyun if self.builder.force_config_on_failure: 451*4882a593Smuzhiyun if failed: 452*4882a593Smuzhiyun do_config = True 453*4882a593Smuzhiyun result.commit_upto = commit_upto 454*4882a593Smuzhiyun if result.return_code < 0: 455*4882a593Smuzhiyun raise ValueError('Interrupt') 456*4882a593Smuzhiyun 457*4882a593Smuzhiyun # We have the build results, so output the result 458*4882a593Smuzhiyun self._WriteResult(result, job.keep_outputs) 459*4882a593Smuzhiyun self.builder.out_queue.put(result) 460*4882a593Smuzhiyun else: 461*4882a593Smuzhiyun # Just build the currently checked-out build 462*4882a593Smuzhiyun result, request_config = self.RunCommit(None, brd, work_dir, True, 463*4882a593Smuzhiyun self.builder.config_only, True, 464*4882a593Smuzhiyun self.builder.force_build_failures) 465*4882a593Smuzhiyun result.commit_upto = 0 466*4882a593Smuzhiyun self._WriteResult(result, job.keep_outputs) 467*4882a593Smuzhiyun self.builder.out_queue.put(result) 468*4882a593Smuzhiyun 469*4882a593Smuzhiyun def run(self): 470*4882a593Smuzhiyun """Our thread's run function 471*4882a593Smuzhiyun 472*4882a593Smuzhiyun This thread picks a job from the queue, runs it, and then goes to the 473*4882a593Smuzhiyun next job. 474*4882a593Smuzhiyun """ 475*4882a593Smuzhiyun while True: 476*4882a593Smuzhiyun job = self.builder.queue.get() 477*4882a593Smuzhiyun self.RunJob(job) 478*4882a593Smuzhiyun self.builder.queue.task_done() 479