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