xref: /rk3399_rockchip-uboot/tools/buildman/builderthread.py (revision f79f1e0c0ea06de3af79094bc80be6e218b5f6ef)
1190064b4SSimon Glass# Copyright (c) 2014 Google, Inc
2190064b4SSimon Glass#
3190064b4SSimon Glass# SPDX-License-Identifier:      GPL-2.0+
4190064b4SSimon Glass#
5190064b4SSimon Glass
6190064b4SSimon Glassimport errno
7190064b4SSimon Glassimport glob
8190064b4SSimon Glassimport os
9190064b4SSimon Glassimport shutil
10190064b4SSimon Glassimport threading
11190064b4SSimon Glass
12190064b4SSimon Glassimport command
13190064b4SSimon Glassimport gitutil
14190064b4SSimon Glass
1588c8dcf9SSimon GlassRETURN_CODE_RETRY = -1
1688c8dcf9SSimon Glass
17f3d015cbSThierry Redingdef Mkdir(dirname, parents = False):
18190064b4SSimon Glass    """Make a directory if it doesn't already exist.
19190064b4SSimon Glass
20190064b4SSimon Glass    Args:
21190064b4SSimon Glass        dirname: Directory to create
22190064b4SSimon Glass    """
23190064b4SSimon Glass    try:
24f3d015cbSThierry Reding        if parents:
25f3d015cbSThierry Reding            os.makedirs(dirname)
26f3d015cbSThierry Reding        else:
27190064b4SSimon Glass            os.mkdir(dirname)
28190064b4SSimon Glass    except OSError as err:
29190064b4SSimon Glass        if err.errno == errno.EEXIST:
30190064b4SSimon Glass            pass
31190064b4SSimon Glass        else:
32190064b4SSimon Glass            raise
33190064b4SSimon Glass
34190064b4SSimon Glassclass BuilderJob:
35190064b4SSimon Glass    """Holds information about a job to be performed by a thread
36190064b4SSimon Glass
37190064b4SSimon Glass    Members:
38190064b4SSimon Glass        board: Board object to build
39190064b4SSimon Glass        commits: List of commit options to build.
40190064b4SSimon Glass    """
41190064b4SSimon Glass    def __init__(self):
42190064b4SSimon Glass        self.board = None
43190064b4SSimon Glass        self.commits = []
44190064b4SSimon Glass
45190064b4SSimon Glass
46190064b4SSimon Glassclass ResultThread(threading.Thread):
47190064b4SSimon Glass    """This thread processes results from builder threads.
48190064b4SSimon Glass
49190064b4SSimon Glass    It simply passes the results on to the builder. There is only one
50190064b4SSimon Glass    result thread, and this helps to serialise the build output.
51190064b4SSimon Glass    """
52190064b4SSimon Glass    def __init__(self, builder):
53190064b4SSimon Glass        """Set up a new result thread
54190064b4SSimon Glass
55190064b4SSimon Glass        Args:
56190064b4SSimon Glass            builder: Builder which will be sent each result
57190064b4SSimon Glass        """
58190064b4SSimon Glass        threading.Thread.__init__(self)
59190064b4SSimon Glass        self.builder = builder
60190064b4SSimon Glass
61190064b4SSimon Glass    def run(self):
62190064b4SSimon Glass        """Called to start up the result thread.
63190064b4SSimon Glass
64190064b4SSimon Glass        We collect the next result job and pass it on to the build.
65190064b4SSimon Glass        """
66190064b4SSimon Glass        while True:
67190064b4SSimon Glass            result = self.builder.out_queue.get()
68190064b4SSimon Glass            self.builder.ProcessResult(result)
69190064b4SSimon Glass            self.builder.out_queue.task_done()
70190064b4SSimon Glass
71190064b4SSimon Glass
72190064b4SSimon Glassclass BuilderThread(threading.Thread):
73190064b4SSimon Glass    """This thread builds U-Boot for a particular board.
74190064b4SSimon Glass
75190064b4SSimon Glass    An input queue provides each new job. We run 'make' to build U-Boot
76190064b4SSimon Glass    and then pass the results on to the output queue.
77190064b4SSimon Glass
78190064b4SSimon Glass    Members:
79190064b4SSimon Glass        builder: The builder which contains information we might need
80190064b4SSimon Glass        thread_num: Our thread number (0-n-1), used to decide on a
81190064b4SSimon Glass                temporary directory
82190064b4SSimon Glass    """
83*f79f1e0cSStephen Warren    def __init__(self, builder, thread_num, incremental, per_board_out_dir):
84190064b4SSimon Glass        """Set up a new builder thread"""
85190064b4SSimon Glass        threading.Thread.__init__(self)
86190064b4SSimon Glass        self.builder = builder
87190064b4SSimon Glass        self.thread_num = thread_num
88*f79f1e0cSStephen Warren        self.incremental = incremental
89*f79f1e0cSStephen Warren        self.per_board_out_dir = per_board_out_dir
90190064b4SSimon Glass
91190064b4SSimon Glass    def Make(self, commit, brd, stage, cwd, *args, **kwargs):
92190064b4SSimon Glass        """Run 'make' on a particular commit and board.
93190064b4SSimon Glass
94190064b4SSimon Glass        The source code will already be checked out, so the 'commit'
95190064b4SSimon Glass        argument is only for information.
96190064b4SSimon Glass
97190064b4SSimon Glass        Args:
98190064b4SSimon Glass            commit: Commit object that is being built
99190064b4SSimon Glass            brd: Board object that is being built
100190064b4SSimon Glass            stage: Stage of the build. Valid stages are:
101fd18a89eSRoger Meier                        mrproper - can be called to clean source
102190064b4SSimon Glass                        config - called to configure for a board
103190064b4SSimon Glass                        build - the main make invocation - it does the build
104190064b4SSimon Glass            args: A list of arguments to pass to 'make'
105190064b4SSimon Glass            kwargs: A list of keyword arguments to pass to command.RunPipe()
106190064b4SSimon Glass
107190064b4SSimon Glass        Returns:
108190064b4SSimon Glass            CommandResult object
109190064b4SSimon Glass        """
110190064b4SSimon Glass        return self.builder.do_make(commit, brd, stage, cwd, *args,
111190064b4SSimon Glass                **kwargs)
112190064b4SSimon Glass
113190064b4SSimon Glass    def RunCommit(self, commit_upto, brd, work_dir, do_config, force_build,
114190064b4SSimon Glass                  force_build_failures):
115190064b4SSimon Glass        """Build a particular commit.
116190064b4SSimon Glass
117190064b4SSimon Glass        If the build is already done, and we are not forcing a build, we skip
118190064b4SSimon Glass        the build and just return the previously-saved results.
119190064b4SSimon Glass
120190064b4SSimon Glass        Args:
121190064b4SSimon Glass            commit_upto: Commit number to build (0...n-1)
122190064b4SSimon Glass            brd: Board object to build
123190064b4SSimon Glass            work_dir: Directory to which the source will be checked out
124190064b4SSimon Glass            do_config: True to run a make <board>_defconfig on the source
125190064b4SSimon Glass            force_build: Force a build even if one was previously done
126190064b4SSimon Glass            force_build_failures: Force a bulid if the previous result showed
127190064b4SSimon Glass                failure
128190064b4SSimon Glass
129190064b4SSimon Glass        Returns:
130190064b4SSimon Glass            tuple containing:
131190064b4SSimon Glass                - CommandResult object containing the results of the build
132190064b4SSimon Glass                - boolean indicating whether 'make config' is still needed
133190064b4SSimon Glass        """
134190064b4SSimon Glass        # Create a default result - it will be overwritte by the call to
135190064b4SSimon Glass        # self.Make() below, in the event that we do a build.
136190064b4SSimon Glass        result = command.CommandResult()
137190064b4SSimon Glass        result.return_code = 0
138190064b4SSimon Glass        if self.builder.in_tree:
139190064b4SSimon Glass            out_dir = work_dir
140190064b4SSimon Glass        else:
141*f79f1e0cSStephen Warren            if self.per_board_out_dir:
142*f79f1e0cSStephen Warren                out_rel_dir = os.path.join('..', brd.target)
143*f79f1e0cSStephen Warren            else:
144*f79f1e0cSStephen Warren                out_rel_dir = 'build'
145*f79f1e0cSStephen Warren            out_dir = os.path.join(work_dir, out_rel_dir)
146190064b4SSimon Glass
147190064b4SSimon Glass        # Check if the job was already completed last time
148190064b4SSimon Glass        done_file = self.builder.GetDoneFile(commit_upto, brd.target)
149190064b4SSimon Glass        result.already_done = os.path.exists(done_file)
150190064b4SSimon Glass        will_build = (force_build or force_build_failures or
151190064b4SSimon Glass            not result.already_done)
152fb3954f9SSimon Glass        if result.already_done:
153190064b4SSimon Glass            # Get the return code from that build and use it
154190064b4SSimon Glass            with open(done_file, 'r') as fd:
155190064b4SSimon Glass                result.return_code = int(fd.readline())
15688c8dcf9SSimon Glass
15788c8dcf9SSimon Glass            # Check the signal that the build needs to be retried
15888c8dcf9SSimon Glass            if result.return_code == RETURN_CODE_RETRY:
15988c8dcf9SSimon Glass                will_build = True
16088c8dcf9SSimon Glass            elif will_build:
161190064b4SSimon Glass                err_file = self.builder.GetErrFile(commit_upto, brd.target)
162190064b4SSimon Glass                if os.path.exists(err_file) and os.stat(err_file).st_size:
163190064b4SSimon Glass                    result.stderr = 'bad'
164190064b4SSimon Glass                elif not force_build:
165190064b4SSimon Glass                    # The build passed, so no need to build it again
166190064b4SSimon Glass                    will_build = False
167190064b4SSimon Glass
168190064b4SSimon Glass        if will_build:
169190064b4SSimon Glass            # We are going to have to build it. First, get a toolchain
170190064b4SSimon Glass            if not self.toolchain:
171190064b4SSimon Glass                try:
172190064b4SSimon Glass                    self.toolchain = self.builder.toolchains.Select(brd.arch)
173190064b4SSimon Glass                except ValueError as err:
174190064b4SSimon Glass                    result.return_code = 10
175190064b4SSimon Glass                    result.stdout = ''
176190064b4SSimon Glass                    result.stderr = str(err)
177190064b4SSimon Glass                    # TODO(sjg@chromium.org): This gets swallowed, but needs
178190064b4SSimon Glass                    # to be reported.
179190064b4SSimon Glass
180190064b4SSimon Glass            if self.toolchain:
181190064b4SSimon Glass                # Checkout the right commit
182190064b4SSimon Glass                if self.builder.commits:
183190064b4SSimon Glass                    commit = self.builder.commits[commit_upto]
184190064b4SSimon Glass                    if self.builder.checkout:
185190064b4SSimon Glass                        git_dir = os.path.join(work_dir, '.git')
186190064b4SSimon Glass                        gitutil.Checkout(commit.hash, git_dir, work_dir,
187190064b4SSimon Glass                                         force=True)
188190064b4SSimon Glass                else:
189190064b4SSimon Glass                    commit = 'current'
190190064b4SSimon Glass
191190064b4SSimon Glass                # Set up the environment and command line
192bb1501f2SSimon Glass                env = self.toolchain.MakeEnvironment(self.builder.full_path)
193190064b4SSimon Glass                Mkdir(out_dir)
194190064b4SSimon Glass                args = []
195190064b4SSimon Glass                cwd = work_dir
19648c1b6a8SSimon Glass                src_dir = os.path.realpath(work_dir)
197190064b4SSimon Glass                if not self.builder.in_tree:
198190064b4SSimon Glass                    if commit_upto is None:
199190064b4SSimon Glass                        # In this case we are building in the original source
200190064b4SSimon Glass                        # directory (i.e. the current directory where buildman
201190064b4SSimon Glass                        # is invoked. The output directory is set to this
202190064b4SSimon Glass                        # thread's selected work directory.
203190064b4SSimon Glass                        #
204190064b4SSimon Glass                        # Symlinks can confuse U-Boot's Makefile since
205190064b4SSimon Glass                        # we may use '..' in our path, so remove them.
206*f79f1e0cSStephen Warren                        out_dir = os.path.realpath(out_dir)
207*f79f1e0cSStephen Warren                        args.append('O=%s' % out_dir)
208190064b4SSimon Glass                        cwd = None
20948c1b6a8SSimon Glass                        src_dir = os.getcwd()
210190064b4SSimon Glass                    else:
211*f79f1e0cSStephen Warren                        args.append('O=%s' % out_rel_dir)
212f5e5ece0STom Rini                if self.builder.verbose_build:
213f5e5ece0STom Rini                    args.append('V=1')
214f5e5ece0STom Rini                else:
215190064b4SSimon Glass                    args.append('-s')
216190064b4SSimon Glass                if self.builder.num_jobs is not None:
217190064b4SSimon Glass                    args.extend(['-j', str(self.builder.num_jobs)])
218190064b4SSimon Glass                config_args = ['%s_defconfig' % brd.target]
219190064b4SSimon Glass                config_out = ''
220190064b4SSimon Glass                args.extend(self.builder.toolchains.GetMakeArguments(brd))
221190064b4SSimon Glass
222190064b4SSimon Glass                # If we need to reconfigure, do that now
223190064b4SSimon Glass                if do_config:
224*f79f1e0cSStephen Warren                    config_out = ''
225*f79f1e0cSStephen Warren                    if not self.incremental:
226fd18a89eSRoger Meier                        result = self.Make(commit, brd, 'mrproper', cwd,
227fd18a89eSRoger Meier                                'mrproper', *args, env=env)
228*f79f1e0cSStephen Warren                        config_out += result.combined
229190064b4SSimon Glass                    result = self.Make(commit, brd, 'config', cwd,
230190064b4SSimon Glass                            *(args + config_args), env=env)
23140f11fceSSimon Glass                    config_out += result.combined
232190064b4SSimon Glass                    do_config = False   # No need to configure next time
233190064b4SSimon Glass                if result.return_code == 0:
234190064b4SSimon Glass                    result = self.Make(commit, brd, 'build', cwd, *args,
235190064b4SSimon Glass                            env=env)
23648c1b6a8SSimon Glass                result.stderr = result.stderr.replace(src_dir + '/', '')
23740f11fceSSimon Glass                if self.builder.verbose_build:
23840f11fceSSimon Glass                    result.stdout = config_out + result.stdout
239190064b4SSimon Glass            else:
240190064b4SSimon Glass                result.return_code = 1
241190064b4SSimon Glass                result.stderr = 'No tool chain for %s\n' % brd.arch
242190064b4SSimon Glass            result.already_done = False
243190064b4SSimon Glass
244190064b4SSimon Glass        result.toolchain = self.toolchain
245190064b4SSimon Glass        result.brd = brd
246190064b4SSimon Glass        result.commit_upto = commit_upto
247190064b4SSimon Glass        result.out_dir = out_dir
248190064b4SSimon Glass        return result, do_config
249190064b4SSimon Glass
250190064b4SSimon Glass    def _WriteResult(self, result, keep_outputs):
251190064b4SSimon Glass        """Write a built result to the output directory.
252190064b4SSimon Glass
253190064b4SSimon Glass        Args:
254190064b4SSimon Glass            result: CommandResult object containing result to write
255190064b4SSimon Glass            keep_outputs: True to store the output binaries, False
256190064b4SSimon Glass                to delete them
257190064b4SSimon Glass        """
258190064b4SSimon Glass        # Fatal error
259190064b4SSimon Glass        if result.return_code < 0:
260190064b4SSimon Glass            return
261190064b4SSimon Glass
26288c8dcf9SSimon Glass        # If we think this might have been aborted with Ctrl-C, record the
26388c8dcf9SSimon Glass        # failure but not that we are 'done' with this board. A retry may fix
26488c8dcf9SSimon Glass        # it.
26588c8dcf9SSimon Glass        maybe_aborted =  result.stderr and 'No child processes' in result.stderr
266190064b4SSimon Glass
267190064b4SSimon Glass        if result.already_done:
268190064b4SSimon Glass            return
269190064b4SSimon Glass
270190064b4SSimon Glass        # Write the output and stderr
271190064b4SSimon Glass        output_dir = self.builder._GetOutputDir(result.commit_upto)
272190064b4SSimon Glass        Mkdir(output_dir)
273190064b4SSimon Glass        build_dir = self.builder.GetBuildDir(result.commit_upto,
274190064b4SSimon Glass                result.brd.target)
275190064b4SSimon Glass        Mkdir(build_dir)
276190064b4SSimon Glass
277190064b4SSimon Glass        outfile = os.path.join(build_dir, 'log')
278190064b4SSimon Glass        with open(outfile, 'w') as fd:
279190064b4SSimon Glass            if result.stdout:
280190064b4SSimon Glass                fd.write(result.stdout)
281190064b4SSimon Glass
282190064b4SSimon Glass        errfile = self.builder.GetErrFile(result.commit_upto,
283190064b4SSimon Glass                result.brd.target)
284190064b4SSimon Glass        if result.stderr:
285190064b4SSimon Glass            with open(errfile, 'w') as fd:
286190064b4SSimon Glass                fd.write(result.stderr)
287190064b4SSimon Glass        elif os.path.exists(errfile):
288190064b4SSimon Glass            os.remove(errfile)
289190064b4SSimon Glass
290190064b4SSimon Glass        if result.toolchain:
291190064b4SSimon Glass            # Write the build result and toolchain information.
292190064b4SSimon Glass            done_file = self.builder.GetDoneFile(result.commit_upto,
293190064b4SSimon Glass                    result.brd.target)
294190064b4SSimon Glass            with open(done_file, 'w') as fd:
29588c8dcf9SSimon Glass                if maybe_aborted:
29688c8dcf9SSimon Glass                    # Special code to indicate we need to retry
29788c8dcf9SSimon Glass                    fd.write('%s' % RETURN_CODE_RETRY)
29888c8dcf9SSimon Glass                else:
299190064b4SSimon Glass                    fd.write('%s' % result.return_code)
300190064b4SSimon Glass            with open(os.path.join(build_dir, 'toolchain'), 'w') as fd:
301190064b4SSimon Glass                print >>fd, 'gcc', result.toolchain.gcc
302190064b4SSimon Glass                print >>fd, 'path', result.toolchain.path
303190064b4SSimon Glass                print >>fd, 'cross', result.toolchain.cross
304190064b4SSimon Glass                print >>fd, 'arch', result.toolchain.arch
305190064b4SSimon Glass                fd.write('%s' % result.return_code)
306190064b4SSimon Glass
307190064b4SSimon Glass            with open(os.path.join(build_dir, 'toolchain'), 'w') as fd:
308190064b4SSimon Glass                print >>fd, 'gcc', result.toolchain.gcc
309190064b4SSimon Glass                print >>fd, 'path', result.toolchain.path
310190064b4SSimon Glass
311190064b4SSimon Glass            # Write out the image and function size information and an objdump
312bb1501f2SSimon Glass            env = result.toolchain.MakeEnvironment(self.builder.full_path)
313190064b4SSimon Glass            lines = []
314190064b4SSimon Glass            for fname in ['u-boot', 'spl/u-boot-spl']:
315190064b4SSimon Glass                cmd = ['%snm' % self.toolchain.cross, '--size-sort', fname]
316190064b4SSimon Glass                nm_result = command.RunPipe([cmd], capture=True,
317190064b4SSimon Glass                        capture_stderr=True, cwd=result.out_dir,
318190064b4SSimon Glass                        raise_on_error=False, env=env)
319190064b4SSimon Glass                if nm_result.stdout:
320190064b4SSimon Glass                    nm = self.builder.GetFuncSizesFile(result.commit_upto,
321190064b4SSimon Glass                                    result.brd.target, fname)
322190064b4SSimon Glass                    with open(nm, 'w') as fd:
323190064b4SSimon Glass                        print >>fd, nm_result.stdout,
324190064b4SSimon Glass
325190064b4SSimon Glass                cmd = ['%sobjdump' % self.toolchain.cross, '-h', fname]
326190064b4SSimon Glass                dump_result = command.RunPipe([cmd], capture=True,
327190064b4SSimon Glass                        capture_stderr=True, cwd=result.out_dir,
328190064b4SSimon Glass                        raise_on_error=False, env=env)
329190064b4SSimon Glass                rodata_size = ''
330190064b4SSimon Glass                if dump_result.stdout:
331190064b4SSimon Glass                    objdump = self.builder.GetObjdumpFile(result.commit_upto,
332190064b4SSimon Glass                                    result.brd.target, fname)
333190064b4SSimon Glass                    with open(objdump, 'w') as fd:
334190064b4SSimon Glass                        print >>fd, dump_result.stdout,
335190064b4SSimon Glass                    for line in dump_result.stdout.splitlines():
336190064b4SSimon Glass                        fields = line.split()
337190064b4SSimon Glass                        if len(fields) > 5 and fields[1] == '.rodata':
338190064b4SSimon Glass                            rodata_size = fields[2]
339190064b4SSimon Glass
340190064b4SSimon Glass                cmd = ['%ssize' % self.toolchain.cross, fname]
341190064b4SSimon Glass                size_result = command.RunPipe([cmd], capture=True,
342190064b4SSimon Glass                        capture_stderr=True, cwd=result.out_dir,
343190064b4SSimon Glass                        raise_on_error=False, env=env)
344190064b4SSimon Glass                if size_result.stdout:
345190064b4SSimon Glass                    lines.append(size_result.stdout.splitlines()[1] + ' ' +
346190064b4SSimon Glass                                 rodata_size)
347190064b4SSimon Glass
348190064b4SSimon Glass            # Write out the image sizes file. This is similar to the output
349190064b4SSimon Glass            # of binutil's 'size' utility, but it omits the header line and
350190064b4SSimon Glass            # adds an additional hex value at the end of each line for the
351190064b4SSimon Glass            # rodata size
352190064b4SSimon Glass            if len(lines):
353190064b4SSimon Glass                sizes = self.builder.GetSizesFile(result.commit_upto,
354190064b4SSimon Glass                                result.brd.target)
355190064b4SSimon Glass                with open(sizes, 'w') as fd:
356190064b4SSimon Glass                    print >>fd, '\n'.join(lines)
357190064b4SSimon Glass
358970f932aSSimon Glass        # Write out the configuration files, with a special case for SPL
359970f932aSSimon Glass        for dirname in ['', 'spl', 'tpl']:
360970f932aSSimon Glass            self.CopyFiles(result.out_dir, build_dir, dirname, ['u-boot.cfg',
361970f932aSSimon Glass                'spl/u-boot-spl.cfg', 'tpl/u-boot-tpl.cfg', '.config',
362970f932aSSimon Glass                'include/autoconf.mk', 'include/generated/autoconf.h'])
363970f932aSSimon Glass
364190064b4SSimon Glass        # Now write the actual build output
365190064b4SSimon Glass        if keep_outputs:
3660eb4c045STom Rini            self.CopyFiles(result.out_dir, build_dir, '', ['u-boot*', '*.bin',
367dd592110STom Rini                '*.map', '*.img', 'MLO', 'SPL', 'include/autoconf.mk',
3680eb4c045STom Rini                'spl/u-boot-spl*'])
369190064b4SSimon Glass
370970f932aSSimon Glass    def CopyFiles(self, out_dir, build_dir, dirname, patterns):
371970f932aSSimon Glass        """Copy files from the build directory to the output.
372970f932aSSimon Glass
373970f932aSSimon Glass        Args:
374970f932aSSimon Glass            out_dir: Path to output directory containing the files
375970f932aSSimon Glass            build_dir: Place to copy the files
376970f932aSSimon Glass            dirname: Source directory, '' for normal U-Boot, 'spl' for SPL
377970f932aSSimon Glass            patterns: A list of filenames (strings) to copy, each relative
378970f932aSSimon Glass               to the build directory
379970f932aSSimon Glass        """
380970f932aSSimon Glass        for pattern in patterns:
381970f932aSSimon Glass            file_list = glob.glob(os.path.join(out_dir, dirname, pattern))
382970f932aSSimon Glass            for fname in file_list:
383970f932aSSimon Glass                target = os.path.basename(fname)
384970f932aSSimon Glass                if dirname:
385970f932aSSimon Glass                    base, ext = os.path.splitext(target)
386970f932aSSimon Glass                    if ext:
387970f932aSSimon Glass                        target = '%s-%s%s' % (base, dirname, ext)
388970f932aSSimon Glass                shutil.copy(fname, os.path.join(build_dir, target))
389190064b4SSimon Glass
390190064b4SSimon Glass    def RunJob(self, job):
391190064b4SSimon Glass        """Run a single job
392190064b4SSimon Glass
393190064b4SSimon Glass        A job consists of a building a list of commits for a particular board.
394190064b4SSimon Glass
395190064b4SSimon Glass        Args:
396190064b4SSimon Glass            job: Job to build
397190064b4SSimon Glass        """
398190064b4SSimon Glass        brd = job.board
399190064b4SSimon Glass        work_dir = self.builder.GetThreadDir(self.thread_num)
400190064b4SSimon Glass        self.toolchain = None
401190064b4SSimon Glass        if job.commits:
402190064b4SSimon Glass            # Run 'make board_defconfig' on the first commit
403190064b4SSimon Glass            do_config = True
404190064b4SSimon Glass            commit_upto  = 0
405190064b4SSimon Glass            force_build = False
406190064b4SSimon Glass            for commit_upto in range(0, len(job.commits), job.step):
407190064b4SSimon Glass                result, request_config = self.RunCommit(commit_upto, brd,
408190064b4SSimon Glass                        work_dir, do_config,
409190064b4SSimon Glass                        force_build or self.builder.force_build,
410190064b4SSimon Glass                        self.builder.force_build_failures)
411190064b4SSimon Glass                failed = result.return_code or result.stderr
412190064b4SSimon Glass                did_config = do_config
413190064b4SSimon Glass                if failed and not do_config:
414190064b4SSimon Glass                    # If our incremental build failed, try building again
415190064b4SSimon Glass                    # with a reconfig.
416190064b4SSimon Glass                    if self.builder.force_config_on_failure:
417190064b4SSimon Glass                        result, request_config = self.RunCommit(commit_upto,
418190064b4SSimon Glass                            brd, work_dir, True, True, False)
419190064b4SSimon Glass                        did_config = True
420190064b4SSimon Glass                if not self.builder.force_reconfig:
421190064b4SSimon Glass                    do_config = request_config
422190064b4SSimon Glass
423190064b4SSimon Glass                # If we built that commit, then config is done. But if we got
424190064b4SSimon Glass                # an warning, reconfig next time to force it to build the same
425190064b4SSimon Glass                # files that created warnings this time. Otherwise an
426190064b4SSimon Glass                # incremental build may not build the same file, and we will
427190064b4SSimon Glass                # think that the warning has gone away.
428190064b4SSimon Glass                # We could avoid this by using -Werror everywhere...
429190064b4SSimon Glass                # For errors, the problem doesn't happen, since presumably
430190064b4SSimon Glass                # the build stopped and didn't generate output, so will retry
431190064b4SSimon Glass                # that file next time. So we could detect warnings and deal
432190064b4SSimon Glass                # with them specially here. For now, we just reconfigure if
433190064b4SSimon Glass                # anything goes work.
434190064b4SSimon Glass                # Of course this is substantially slower if there are build
435190064b4SSimon Glass                # errors/warnings (e.g. 2-3x slower even if only 10% of builds
436190064b4SSimon Glass                # have problems).
437190064b4SSimon Glass                if (failed and not result.already_done and not did_config and
438190064b4SSimon Glass                        self.builder.force_config_on_failure):
439190064b4SSimon Glass                    # If this build failed, try the next one with a
440190064b4SSimon Glass                    # reconfigure.
441190064b4SSimon Glass                    # Sometimes if the board_config.h file changes it can mess
442190064b4SSimon Glass                    # with dependencies, and we get:
443190064b4SSimon Glass                    # make: *** No rule to make target `include/autoconf.mk',
444190064b4SSimon Glass                    #     needed by `depend'.
445190064b4SSimon Glass                    do_config = True
446190064b4SSimon Glass                    force_build = True
447190064b4SSimon Glass                else:
448190064b4SSimon Glass                    force_build = False
449190064b4SSimon Glass                    if self.builder.force_config_on_failure:
450190064b4SSimon Glass                        if failed:
451190064b4SSimon Glass                            do_config = True
452190064b4SSimon Glass                    result.commit_upto = commit_upto
453190064b4SSimon Glass                    if result.return_code < 0:
454190064b4SSimon Glass                        raise ValueError('Interrupt')
455190064b4SSimon Glass
456190064b4SSimon Glass                # We have the build results, so output the result
457190064b4SSimon Glass                self._WriteResult(result, job.keep_outputs)
458190064b4SSimon Glass                self.builder.out_queue.put(result)
459190064b4SSimon Glass        else:
460190064b4SSimon Glass            # Just build the currently checked-out build
461190064b4SSimon Glass            result, request_config = self.RunCommit(None, brd, work_dir, True,
462190064b4SSimon Glass                        True, self.builder.force_build_failures)
463190064b4SSimon Glass            result.commit_upto = 0
464190064b4SSimon Glass            self._WriteResult(result, job.keep_outputs)
465190064b4SSimon Glass            self.builder.out_queue.put(result)
466190064b4SSimon Glass
467190064b4SSimon Glass    def run(self):
468190064b4SSimon Glass        """Our thread's run function
469190064b4SSimon Glass
470190064b4SSimon Glass        This thread picks a job from the queue, runs it, and then goes to the
471190064b4SSimon Glass        next job.
472190064b4SSimon Glass        """
473190064b4SSimon Glass        alive = True
474190064b4SSimon Glass        while True:
475190064b4SSimon Glass            job = self.builder.queue.get()
476190064b4SSimon Glass            if self.builder.active and alive:
477190064b4SSimon Glass                self.RunJob(job)
478190064b4SSimon Glass            '''
479190064b4SSimon Glass            try:
480190064b4SSimon Glass                if self.builder.active and alive:
481190064b4SSimon Glass                    self.RunJob(job)
482190064b4SSimon Glass            except Exception as err:
483190064b4SSimon Glass                alive = False
484190064b4SSimon Glass                print err
485190064b4SSimon Glass            '''
486190064b4SSimon Glass            self.builder.queue.task_done()
487