mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 14:00:19 +00:00 
			
		
		
		
	The host compiler is not failing on warnings at present, when the -E flag is used in buildman. Add the required flag to fix this. Signed-off-by: Simon Glass <sjg@chromium.org> Tested-by: Tom Rini <trini@konsulko.com>
		
			
				
	
	
		
			605 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			605 lines
		
	
	
		
			26 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 gitutil
 | 
						|
from u_boot_pylib import command
 | 
						|
 | 
						|
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')
 | 
						|
                    args.append('HOSTCFLAGS=-Werror')
 | 
						|
                if self.builder.allow_missing:
 | 
						|
                    args.append('BINMAN_ALLOW_MISSING=1')
 | 
						|
                if self.builder.no_lto:
 | 
						|
                    args.append('NO_LTO=1')
 | 
						|
                if self.builder.reproducible_builds:
 | 
						|
                    args.append('SOURCE_DATE_EPOCH=0')
 | 
						|
                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')
 | 
						|
                cmd_list = []
 | 
						|
                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
 | 
						|
                        cmd_list.append([self.builder.gnu_make, 'mrproper',
 | 
						|
                                         *args])
 | 
						|
                    result = self.Make(commit, brd, 'config', cwd,
 | 
						|
                            *(args + config_args), env=env)
 | 
						|
                    cmd_list.append([self.builder.gnu_make] + args +
 | 
						|
                                    config_args)
 | 
						|
                    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)
 | 
						|
                    cmd_list.append([self.builder.gnu_make] + args)
 | 
						|
                    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
 | 
						|
                result.cmd_list = cmd_list
 | 
						|
            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]))
 | 
						|
 | 
						|
            with open(os.path.join(build_dir, 'out-cmd'), 'w',
 | 
						|
                      encoding='utf-8') as fd:
 | 
						|
                for cmd in result.cmd_list:
 | 
						|
                    print(' '.join(cmd), file=fd)
 | 
						|
 | 
						|
            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()
 |