587 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			587 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Python
		
	
	
	
| # SPDX-License-Identifier: GPL-2.0+
 | |
| # Copyright (c) 2014 Google, Inc
 | |
| #
 | |
| 
 | |
| import errno
 | |
| import glob
 | |
| import os
 | |
| import shutil
 | |
| import sys
 | |
| import threading
 | |
| 
 | |
| from buildman import cfgutil
 | |
| from patman import command
 | |
| from patman import gitutil
 | |
| 
 | |
| RETURN_CODE_RETRY = -1
 | |
| BASE_ELF_FILENAMES = ['u-boot', 'spl/u-boot-spl', 'tpl/u-boot-tpl']
 | |
| 
 | |
| def Mkdir(dirname, parents = False):
 | |
|     """Make a directory if it doesn't already exist.
 | |
| 
 | |
|     Args:
 | |
|         dirname: Directory to create
 | |
|     """
 | |
|     try:
 | |
|         if parents:
 | |
|             os.makedirs(dirname)
 | |
|         else:
 | |
|             os.mkdir(dirname)
 | |
|     except OSError as err:
 | |
|         if err.errno == errno.EEXIST:
 | |
|             if os.path.realpath('.') == os.path.realpath(dirname):
 | |
|                 print("Cannot create the current working directory '%s'!" % dirname)
 | |
|                 sys.exit(1)
 | |
|             pass
 | |
|         else:
 | |
|             raise
 | |
| 
 | |
| class BuilderJob:
 | |
|     """Holds information about a job to be performed by a thread
 | |
| 
 | |
|     Members:
 | |
|         brd: Board object to build
 | |
|         commits: List of Commit objects to build
 | |
|         keep_outputs: True to save build output files
 | |
|         step: 1 to process every commit, n to process every nth commit
 | |
|         work_in_output: Use the output directory as the work directory and
 | |
|             don't write to a separate output directory.
 | |
|     """
 | |
|     def __init__(self):
 | |
|         self.brd = None
 | |
|         self.commits = []
 | |
|         self.keep_outputs = False
 | |
|         self.step = 1
 | |
|         self.work_in_output = False
 | |
| 
 | |
| 
 | |
| class ResultThread(threading.Thread):
 | |
|     """This thread processes results from builder threads.
 | |
| 
 | |
|     It simply passes the results on to the builder. There is only one
 | |
|     result thread, and this helps to serialise the build output.
 | |
|     """
 | |
|     def __init__(self, builder):
 | |
|         """Set up a new result thread
 | |
| 
 | |
|         Args:
 | |
|             builder: Builder which will be sent each result
 | |
|         """
 | |
|         threading.Thread.__init__(self)
 | |
|         self.builder = builder
 | |
| 
 | |
|     def run(self):
 | |
|         """Called to start up the result thread.
 | |
| 
 | |
|         We collect the next result job and pass it on to the build.
 | |
|         """
 | |
|         while True:
 | |
|             result = self.builder.out_queue.get()
 | |
|             self.builder.ProcessResult(result)
 | |
|             self.builder.out_queue.task_done()
 | |
| 
 | |
| 
 | |
| class BuilderThread(threading.Thread):
 | |
|     """This thread builds U-Boot for a particular board.
 | |
| 
 | |
|     An input queue provides each new job. We run 'make' to build U-Boot
 | |
|     and then pass the results on to the output queue.
 | |
| 
 | |
|     Members:
 | |
|         builder: The builder which contains information we might need
 | |
|         thread_num: Our thread number (0-n-1), used to decide on a
 | |
|             temporary directory. If this is -1 then there are no threads
 | |
|             and we are the (only) main process
 | |
|         mrproper: Use 'make mrproper' before each reconfigure
 | |
|         per_board_out_dir: True to build in a separate persistent directory per
 | |
|             board rather than a thread-specific directory
 | |
|         test_exception: Used for testing; True to raise an exception instead of
 | |
|             reporting the build result
 | |
|     """
 | |
|     def __init__(self, builder, thread_num, mrproper, per_board_out_dir,
 | |
|                  test_exception=False):
 | |
|         """Set up a new builder thread"""
 | |
|         threading.Thread.__init__(self)
 | |
|         self.builder = builder
 | |
|         self.thread_num = thread_num
 | |
|         self.mrproper = mrproper
 | |
|         self.per_board_out_dir = per_board_out_dir
 | |
|         self.test_exception = test_exception
 | |
| 
 | |
|     def Make(self, commit, brd, stage, cwd, *args, **kwargs):
 | |
|         """Run 'make' on a particular commit and board.
 | |
| 
 | |
|         The source code will already be checked out, so the 'commit'
 | |
|         argument is only for information.
 | |
| 
 | |
|         Args:
 | |
|             commit: Commit object that is being built
 | |
|             brd: Board object that is being built
 | |
|             stage: Stage of the build. Valid stages are:
 | |
|                         mrproper - can be called to clean source
 | |
|                         config - called to configure for a board
 | |
|                         build - the main make invocation - it does the build
 | |
|             args: A list of arguments to pass to 'make'
 | |
|             kwargs: A list of keyword arguments to pass to command.run_pipe()
 | |
| 
 | |
|         Returns:
 | |
|             CommandResult object
 | |
|         """
 | |
|         return self.builder.do_make(commit, brd, stage, cwd, *args,
 | |
|                 **kwargs)
 | |
| 
 | |
|     def RunCommit(self, commit_upto, brd, work_dir, do_config, config_only,
 | |
|                   force_build, force_build_failures, work_in_output,
 | |
|                   adjust_cfg):
 | |
|         """Build a particular commit.
 | |
| 
 | |
|         If the build is already done, and we are not forcing a build, we skip
 | |
|         the build and just return the previously-saved results.
 | |
| 
 | |
|         Args:
 | |
|             commit_upto: Commit number to build (0...n-1)
 | |
|             brd: Board object to build
 | |
|             work_dir: Directory to which the source will be checked out
 | |
|             do_config: True to run a make <board>_defconfig on the source
 | |
|             config_only: Only configure the source, do not build it
 | |
|             force_build: Force a build even if one was previously done
 | |
|             force_build_failures: Force a bulid if the previous result showed
 | |
|                 failure
 | |
|             work_in_output: Use the output directory as the work directory and
 | |
|                 don't write to a separate output directory.
 | |
|             adjust_cfg (list of str): List of changes to make to .config file
 | |
|                 before building. Each is one of (where C is either CONFIG_xxx
 | |
|                 or just xxx):
 | |
|                      C to enable C
 | |
|                      ~C to disable C
 | |
|                      C=val to set the value of C (val must have quotes if C is
 | |
|                          a string Kconfig
 | |
| 
 | |
|         Returns:
 | |
|             tuple containing:
 | |
|                 - CommandResult object containing the results of the build
 | |
|                 - boolean indicating whether 'make config' is still needed
 | |
|         """
 | |
|         # Create a default result - it will be overwritte by the call to
 | |
|         # self.Make() below, in the event that we do a build.
 | |
|         result = command.CommandResult()
 | |
|         result.return_code = 0
 | |
|         if work_in_output or self.builder.in_tree:
 | |
|             out_dir = work_dir
 | |
|         else:
 | |
|             if self.per_board_out_dir:
 | |
|                 out_rel_dir = os.path.join('..', brd.target)
 | |
|             else:
 | |
|                 out_rel_dir = 'build'
 | |
|             out_dir = os.path.join(work_dir, out_rel_dir)
 | |
| 
 | |
|         # Check if the job was already completed last time
 | |
|         done_file = self.builder.GetDoneFile(commit_upto, brd.target)
 | |
|         result.already_done = os.path.exists(done_file)
 | |
|         will_build = (force_build or force_build_failures or
 | |
|             not result.already_done)
 | |
|         if result.already_done:
 | |
|             # Get the return code from that build and use it
 | |
|             with open(done_file, 'r') as fd:
 | |
|                 try:
 | |
|                     result.return_code = int(fd.readline())
 | |
|                 except ValueError:
 | |
|                     # The file may be empty due to running out of disk space.
 | |
|                     # Try a rebuild
 | |
|                     result.return_code = RETURN_CODE_RETRY
 | |
| 
 | |
|             # Check the signal that the build needs to be retried
 | |
|             if result.return_code == RETURN_CODE_RETRY:
 | |
|                 will_build = True
 | |
|             elif will_build:
 | |
|                 err_file = self.builder.GetErrFile(commit_upto, brd.target)
 | |
|                 if os.path.exists(err_file) and os.stat(err_file).st_size:
 | |
|                     result.stderr = 'bad'
 | |
|                 elif not force_build:
 | |
|                     # The build passed, so no need to build it again
 | |
|                     will_build = False
 | |
| 
 | |
|         if will_build:
 | |
|             # We are going to have to build it. First, get a toolchain
 | |
|             if not self.toolchain:
 | |
|                 try:
 | |
|                     self.toolchain = self.builder.toolchains.Select(brd.arch)
 | |
|                 except ValueError as err:
 | |
|                     result.return_code = 10
 | |
|                     result.stdout = ''
 | |
|                     result.stderr = str(err)
 | |
|                     # TODO(sjg@chromium.org): This gets swallowed, but needs
 | |
|                     # to be reported.
 | |
| 
 | |
|             if self.toolchain:
 | |
|                 # Checkout the right commit
 | |
|                 if self.builder.commits:
 | |
|                     commit = self.builder.commits[commit_upto]
 | |
|                     if self.builder.checkout:
 | |
|                         git_dir = os.path.join(work_dir, '.git')
 | |
|                         gitutil.checkout(commit.hash, git_dir, work_dir,
 | |
|                                          force=True)
 | |
|                 else:
 | |
|                     commit = 'current'
 | |
| 
 | |
|                 # Set up the environment and command line
 | |
|                 env = self.toolchain.MakeEnvironment(self.builder.full_path)
 | |
|                 Mkdir(out_dir)
 | |
|                 args = []
 | |
|                 cwd = work_dir
 | |
|                 src_dir = os.path.realpath(work_dir)
 | |
|                 if not self.builder.in_tree:
 | |
|                     if commit_upto is None:
 | |
|                         # In this case we are building in the original source
 | |
|                         # directory (i.e. the current directory where buildman
 | |
|                         # is invoked. The output directory is set to this
 | |
|                         # thread's selected work directory.
 | |
|                         #
 | |
|                         # Symlinks can confuse U-Boot's Makefile since
 | |
|                         # we may use '..' in our path, so remove them.
 | |
|                         out_dir = os.path.realpath(out_dir)
 | |
|                         args.append('O=%s' % out_dir)
 | |
|                         cwd = None
 | |
|                         src_dir = os.getcwd()
 | |
|                     else:
 | |
|                         args.append('O=%s' % out_rel_dir)
 | |
|                 if self.builder.verbose_build:
 | |
|                     args.append('V=1')
 | |
|                 else:
 | |
|                     args.append('-s')
 | |
|                 if self.builder.num_jobs is not None:
 | |
|                     args.extend(['-j', str(self.builder.num_jobs)])
 | |
|                 if self.builder.warnings_as_errors:
 | |
|                     args.append('KCFLAGS=-Werror')
 | |
|                 if self.builder.allow_missing:
 | |
|                     args.append('BINMAN_ALLOW_MISSING=1')
 | |
|                 config_args = ['%s_defconfig' % brd.target]
 | |
|                 config_out = ''
 | |
|                 args.extend(self.builder.toolchains.GetMakeArguments(brd))
 | |
|                 args.extend(self.toolchain.MakeArgs())
 | |
| 
 | |
|                 # Remove any output targets. Since we use a build directory that
 | |
|                 # was previously used by another board, it may have produced an
 | |
|                 # SPL image. If we don't remove it (i.e. see do_config and
 | |
|                 # self.mrproper below) then it will appear to be the output of
 | |
|                 # this build, even if it does not produce SPL images.
 | |
|                 build_dir = self.builder.GetBuildDir(commit_upto, brd.target)
 | |
|                 for elf in BASE_ELF_FILENAMES:
 | |
|                     fname = os.path.join(out_dir, elf)
 | |
|                     if os.path.exists(fname):
 | |
|                         os.remove(fname)
 | |
| 
 | |
|                 # If we need to reconfigure, do that now
 | |
|                 cfg_file = os.path.join(out_dir, '.config')
 | |
|                 if do_config or adjust_cfg:
 | |
|                     config_out = ''
 | |
|                     if self.mrproper:
 | |
|                         result = self.Make(commit, brd, 'mrproper', cwd,
 | |
|                                 'mrproper', *args, env=env)
 | |
|                         config_out += result.combined
 | |
|                     result = self.Make(commit, brd, 'config', cwd,
 | |
|                             *(args + config_args), env=env)
 | |
|                     config_out += result.combined
 | |
|                     do_config = False   # No need to configure next time
 | |
|                     if adjust_cfg:
 | |
|                         cfgutil.adjust_cfg_file(cfg_file, adjust_cfg)
 | |
|                 if result.return_code == 0:
 | |
|                     if config_only:
 | |
|                         args.append('cfg')
 | |
|                     result = self.Make(commit, brd, 'build', cwd, *args,
 | |
|                             env=env)
 | |
|                     if (result.return_code == 2 and
 | |
|                         ('Some images are invalid' in result.stderr)):
 | |
|                         # This is handled later by the check for output in
 | |
|                         # stderr
 | |
|                         result.return_code = 0
 | |
|                     if adjust_cfg:
 | |
|                         errs = cfgutil.check_cfg_file(cfg_file, adjust_cfg)
 | |
|                         if errs:
 | |
|                             result.stderr += errs
 | |
|                             result.return_code = 1
 | |
|                 result.stderr = result.stderr.replace(src_dir + '/', '')
 | |
|                 if self.builder.verbose_build:
 | |
|                     result.stdout = config_out + result.stdout
 | |
|             else:
 | |
|                 result.return_code = 1
 | |
|                 result.stderr = 'No tool chain for %s\n' % brd.arch
 | |
|             result.already_done = False
 | |
| 
 | |
|         result.toolchain = self.toolchain
 | |
|         result.brd = brd
 | |
|         result.commit_upto = commit_upto
 | |
|         result.out_dir = out_dir
 | |
|         return result, do_config
 | |
| 
 | |
|     def _WriteResult(self, result, keep_outputs, work_in_output):
 | |
|         """Write a built result to the output directory.
 | |
| 
 | |
|         Args:
 | |
|             result: CommandResult object containing result to write
 | |
|             keep_outputs: True to store the output binaries, False
 | |
|                 to delete them
 | |
|             work_in_output: Use the output directory as the work directory and
 | |
|                 don't write to a separate output directory.
 | |
|         """
 | |
|         # If we think this might have been aborted with Ctrl-C, record the
 | |
|         # failure but not that we are 'done' with this board. A retry may fix
 | |
|         # it.
 | |
|         maybe_aborted = result.stderr and 'No child processes' in result.stderr
 | |
| 
 | |
|         if result.return_code >= 0 and result.already_done:
 | |
|             return
 | |
| 
 | |
|         # Write the output and stderr
 | |
|         output_dir = self.builder._GetOutputDir(result.commit_upto)
 | |
|         Mkdir(output_dir)
 | |
|         build_dir = self.builder.GetBuildDir(result.commit_upto,
 | |
|                 result.brd.target)
 | |
|         Mkdir(build_dir)
 | |
| 
 | |
|         outfile = os.path.join(build_dir, 'log')
 | |
|         with open(outfile, 'w') as fd:
 | |
|             if result.stdout:
 | |
|                 fd.write(result.stdout)
 | |
| 
 | |
|         errfile = self.builder.GetErrFile(result.commit_upto,
 | |
|                 result.brd.target)
 | |
|         if result.stderr:
 | |
|             with open(errfile, 'w') as fd:
 | |
|                 fd.write(result.stderr)
 | |
|         elif os.path.exists(errfile):
 | |
|             os.remove(errfile)
 | |
| 
 | |
|         # Fatal error
 | |
|         if result.return_code < 0:
 | |
|             return
 | |
| 
 | |
|         if result.toolchain:
 | |
|             # Write the build result and toolchain information.
 | |
|             done_file = self.builder.GetDoneFile(result.commit_upto,
 | |
|                     result.brd.target)
 | |
|             with open(done_file, 'w') as fd:
 | |
|                 if maybe_aborted:
 | |
|                     # Special code to indicate we need to retry
 | |
|                     fd.write('%s' % RETURN_CODE_RETRY)
 | |
|                 else:
 | |
|                     fd.write('%s' % result.return_code)
 | |
|             with open(os.path.join(build_dir, 'toolchain'), 'w') as fd:
 | |
|                 print('gcc', result.toolchain.gcc, file=fd)
 | |
|                 print('path', result.toolchain.path, file=fd)
 | |
|                 print('cross', result.toolchain.cross, file=fd)
 | |
|                 print('arch', result.toolchain.arch, file=fd)
 | |
|                 fd.write('%s' % result.return_code)
 | |
| 
 | |
|             # Write out the image and function size information and an objdump
 | |
|             env = result.toolchain.MakeEnvironment(self.builder.full_path)
 | |
|             with open(os.path.join(build_dir, 'out-env'), 'wb') as fd:
 | |
|                 for var in sorted(env.keys()):
 | |
|                     fd.write(b'%s="%s"' % (var, env[var]))
 | |
|             lines = []
 | |
|             for fname in BASE_ELF_FILENAMES:
 | |
|                 cmd = ['%snm' % self.toolchain.cross, '--size-sort', fname]
 | |
|                 nm_result = command.run_pipe([cmd], capture=True,
 | |
|                         capture_stderr=True, cwd=result.out_dir,
 | |
|                         raise_on_error=False, env=env)
 | |
|                 if nm_result.stdout:
 | |
|                     nm = self.builder.GetFuncSizesFile(result.commit_upto,
 | |
|                                     result.brd.target, fname)
 | |
|                     with open(nm, 'w') as fd:
 | |
|                         print(nm_result.stdout, end=' ', file=fd)
 | |
| 
 | |
|                 cmd = ['%sobjdump' % self.toolchain.cross, '-h', fname]
 | |
|                 dump_result = command.run_pipe([cmd], capture=True,
 | |
|                         capture_stderr=True, cwd=result.out_dir,
 | |
|                         raise_on_error=False, env=env)
 | |
|                 rodata_size = ''
 | |
|                 if dump_result.stdout:
 | |
|                     objdump = self.builder.GetObjdumpFile(result.commit_upto,
 | |
|                                     result.brd.target, fname)
 | |
|                     with open(objdump, 'w') as fd:
 | |
|                         print(dump_result.stdout, end=' ', file=fd)
 | |
|                     for line in dump_result.stdout.splitlines():
 | |
|                         fields = line.split()
 | |
|                         if len(fields) > 5 and fields[1] == '.rodata':
 | |
|                             rodata_size = fields[2]
 | |
| 
 | |
|                 cmd = ['%ssize' % self.toolchain.cross, fname]
 | |
|                 size_result = command.run_pipe([cmd], capture=True,
 | |
|                         capture_stderr=True, cwd=result.out_dir,
 | |
|                         raise_on_error=False, env=env)
 | |
|                 if size_result.stdout:
 | |
|                     lines.append(size_result.stdout.splitlines()[1] + ' ' +
 | |
|                                  rodata_size)
 | |
| 
 | |
|             # Extract the environment from U-Boot and dump it out
 | |
|             cmd = ['%sobjcopy' % self.toolchain.cross, '-O', 'binary',
 | |
|                    '-j', '.rodata.default_environment',
 | |
|                    'env/built-in.o', 'uboot.env']
 | |
|             command.run_pipe([cmd], capture=True,
 | |
|                             capture_stderr=True, cwd=result.out_dir,
 | |
|                             raise_on_error=False, env=env)
 | |
|             ubootenv = os.path.join(result.out_dir, 'uboot.env')
 | |
|             if not work_in_output:
 | |
|                 self.CopyFiles(result.out_dir, build_dir, '', ['uboot.env'])
 | |
| 
 | |
|             # Write out the image sizes file. This is similar to the output
 | |
|             # of binutil's 'size' utility, but it omits the header line and
 | |
|             # adds an additional hex value at the end of each line for the
 | |
|             # rodata size
 | |
|             if len(lines):
 | |
|                 sizes = self.builder.GetSizesFile(result.commit_upto,
 | |
|                                 result.brd.target)
 | |
|                 with open(sizes, 'w') as fd:
 | |
|                     print('\n'.join(lines), file=fd)
 | |
| 
 | |
|         if not work_in_output:
 | |
|             # Write out the configuration files, with a special case for SPL
 | |
|             for dirname in ['', 'spl', 'tpl']:
 | |
|                 self.CopyFiles(
 | |
|                     result.out_dir, build_dir, dirname,
 | |
|                     ['u-boot.cfg', 'spl/u-boot-spl.cfg', 'tpl/u-boot-tpl.cfg',
 | |
|                      '.config', 'include/autoconf.mk',
 | |
|                      'include/generated/autoconf.h'])
 | |
| 
 | |
|             # Now write the actual build output
 | |
|             if keep_outputs:
 | |
|                 self.CopyFiles(
 | |
|                     result.out_dir, build_dir, '',
 | |
|                     ['u-boot*', '*.bin', '*.map', '*.img', 'MLO', 'SPL',
 | |
|                      'include/autoconf.mk', 'spl/u-boot-spl*'])
 | |
| 
 | |
|     def CopyFiles(self, out_dir, build_dir, dirname, patterns):
 | |
|         """Copy files from the build directory to the output.
 | |
| 
 | |
|         Args:
 | |
|             out_dir: Path to output directory containing the files
 | |
|             build_dir: Place to copy the files
 | |
|             dirname: Source directory, '' for normal U-Boot, 'spl' for SPL
 | |
|             patterns: A list of filenames (strings) to copy, each relative
 | |
|                to the build directory
 | |
|         """
 | |
|         for pattern in patterns:
 | |
|             file_list = glob.glob(os.path.join(out_dir, dirname, pattern))
 | |
|             for fname in file_list:
 | |
|                 target = os.path.basename(fname)
 | |
|                 if dirname:
 | |
|                     base, ext = os.path.splitext(target)
 | |
|                     if ext:
 | |
|                         target = '%s-%s%s' % (base, dirname, ext)
 | |
|                 shutil.copy(fname, os.path.join(build_dir, target))
 | |
| 
 | |
|     def _SendResult(self, result):
 | |
|         """Send a result to the builder for processing
 | |
| 
 | |
|         Args:
 | |
|             result: CommandResult object containing the results of the build
 | |
| 
 | |
|         Raises:
 | |
|             ValueError if self.test_exception is true (for testing)
 | |
|         """
 | |
|         if self.test_exception:
 | |
|             raise ValueError('test exception')
 | |
|         if self.thread_num != -1:
 | |
|             self.builder.out_queue.put(result)
 | |
|         else:
 | |
|             self.builder.ProcessResult(result)
 | |
| 
 | |
|     def RunJob(self, job):
 | |
|         """Run a single job
 | |
| 
 | |
|         A job consists of a building a list of commits for a particular board.
 | |
| 
 | |
|         Args:
 | |
|             job: Job to build
 | |
| 
 | |
|         Returns:
 | |
|             List of Result objects
 | |
|         """
 | |
|         brd = job.brd
 | |
|         work_dir = self.builder.GetThreadDir(self.thread_num)
 | |
|         self.toolchain = None
 | |
|         if job.commits:
 | |
|             # Run 'make board_defconfig' on the first commit
 | |
|             do_config = True
 | |
|             commit_upto  = 0
 | |
|             force_build = False
 | |
|             for commit_upto in range(0, len(job.commits), job.step):
 | |
|                 result, request_config = self.RunCommit(commit_upto, brd,
 | |
|                         work_dir, do_config, self.builder.config_only,
 | |
|                         force_build or self.builder.force_build,
 | |
|                         self.builder.force_build_failures,
 | |
|                         job.work_in_output, job.adjust_cfg)
 | |
|                 failed = result.return_code or result.stderr
 | |
|                 did_config = do_config
 | |
|                 if failed and not do_config:
 | |
|                     # If our incremental build failed, try building again
 | |
|                     # with a reconfig.
 | |
|                     if self.builder.force_config_on_failure:
 | |
|                         result, request_config = self.RunCommit(commit_upto,
 | |
|                             brd, work_dir, True, False, True, False,
 | |
|                             job.work_in_output, job.adjust_cfg)
 | |
|                         did_config = True
 | |
|                 if not self.builder.force_reconfig:
 | |
|                     do_config = request_config
 | |
| 
 | |
|                 # If we built that commit, then config is done. But if we got
 | |
|                 # an warning, reconfig next time to force it to build the same
 | |
|                 # files that created warnings this time. Otherwise an
 | |
|                 # incremental build may not build the same file, and we will
 | |
|                 # think that the warning has gone away.
 | |
|                 # We could avoid this by using -Werror everywhere...
 | |
|                 # For errors, the problem doesn't happen, since presumably
 | |
|                 # the build stopped and didn't generate output, so will retry
 | |
|                 # that file next time. So we could detect warnings and deal
 | |
|                 # with them specially here. For now, we just reconfigure if
 | |
|                 # anything goes work.
 | |
|                 # Of course this is substantially slower if there are build
 | |
|                 # errors/warnings (e.g. 2-3x slower even if only 10% of builds
 | |
|                 # have problems).
 | |
|                 if (failed and not result.already_done and not did_config and
 | |
|                         self.builder.force_config_on_failure):
 | |
|                     # If this build failed, try the next one with a
 | |
|                     # reconfigure.
 | |
|                     # Sometimes if the board_config.h file changes it can mess
 | |
|                     # with dependencies, and we get:
 | |
|                     # make: *** No rule to make target `include/autoconf.mk',
 | |
|                     #     needed by `depend'.
 | |
|                     do_config = True
 | |
|                     force_build = True
 | |
|                 else:
 | |
|                     force_build = False
 | |
|                     if self.builder.force_config_on_failure:
 | |
|                         if failed:
 | |
|                             do_config = True
 | |
|                     result.commit_upto = commit_upto
 | |
|                     if result.return_code < 0:
 | |
|                         raise ValueError('Interrupt')
 | |
| 
 | |
|                 # We have the build results, so output the result
 | |
|                 self._WriteResult(result, job.keep_outputs, job.work_in_output)
 | |
|                 self._SendResult(result)
 | |
|         else:
 | |
|             # Just build the currently checked-out build
 | |
|             result, request_config = self.RunCommit(None, brd, work_dir, True,
 | |
|                         self.builder.config_only, True,
 | |
|                         self.builder.force_build_failures, job.work_in_output,
 | |
|                         job.adjust_cfg)
 | |
|             result.commit_upto = 0
 | |
|             self._WriteResult(result, job.keep_outputs, job.work_in_output)
 | |
|             self._SendResult(result)
 | |
| 
 | |
|     def run(self):
 | |
|         """Our thread's run function
 | |
| 
 | |
|         This thread picks a job from the queue, runs it, and then goes to the
 | |
|         next job.
 | |
|         """
 | |
|         while True:
 | |
|             job = self.builder.queue.get()
 | |
|             try:
 | |
|                 self.RunJob(job)
 | |
|             except Exception as e:
 | |
|                 print('Thread exception (use -T0 to run without threads):', e)
 | |
|                 self.builder.thread_exceptions.append(e)
 | |
|             self.builder.queue.task_done()
 |