Beispiel #1
0
def ShowActions(series, why_selected, boards_selected, builder, options,
                board_warnings):
    """Display a list of actions that we would take, if not a dry run.

    Args:
        series: Series object
        why_selected: Dictionary where each key is a buildman argument
                provided by the user, and the value is the list of boards
                brought in by that argument. For example, 'arm' might bring
                in 400 boards, so in this case the key would be 'arm' and
                the value would be a list of board names.
        boards_selected: Dict of selected boards, key is target name,
                value is Board object
        builder: The builder that will be used to build the commits
        options: Command line options object
        board_warnings: List of warnings obtained from board selected
    """
    col = terminal.Color()
    print('Dry run, so not doing much. But I would do this:')
    print()
    if series:
        commits = series.commits
    else:
        commits = None
    print(GetActionSummary(False, commits, boards_selected, options))
    print('Build directory: %s' % builder.base_dir)
    if commits:
        for upto in range(0, len(series.commits), options.step):
            commit = series.commits[upto]
            print('   ',
                  col.Color(col.YELLOW, commit.hash[:8], bright=False),
                  end=' ')
            print(commit.subject)
    print()
    for arg in why_selected:
        if arg != 'all':
            print(arg, ': %d boards' % len(why_selected[arg]))
            if options.verbose:
                print('   %s' % ' '.join(why_selected[arg]))
    print(('Total boards to build for each commit: %d\n' %
           len(why_selected['all'])))
    if board_warnings:
        for warning in board_warnings:
            print(col.Color(col.YELLOW, warning))
Beispiel #2
0
    def ShowActions(self, args, cmd, process_tags):
        """Show what actions we will/would perform

        Args:
            args: List of patch files we created
            cmd: The git command we would have run
            process_tags: Process tags as if they were aliases
        """
        to_set = set(gitutil.BuildEmailList(self.to))
        cc_set = set(gitutil.BuildEmailList(self.cc))

        col = terminal.Color()
        print('Dry run, so not doing much. But I would do this:')
        print()
        print('Send a total of %d patch%s with %scover letter.' %
              (len(args), '' if len(args) == 1 else 'es',
               self.get('cover') and 'a ' or 'no '))

        # TODO: Colour the patches according to whether they passed checks
        for upto in range(len(args)):
            commit = self.commits[upto]
            print(col.Color(col.GREEN, '   %s' % args[upto]))
            cc_list = list(self._generated_cc[commit.patch])
            for email in set(cc_list) - to_set - cc_set:
                if email == None:
                    email = col.Color(col.YELLOW,
                                      "<alias '%s' not found>" % tag)
                if email:
                    print('      Cc: ', email)
        print
        for item in to_set:
            print('To:\t ', item)
        for item in cc_set - to_set:
            print('Cc:\t ', item)
        print('Version: ', self.get('version'))
        print('Prefix:\t ', self.get('prefix'))
        if self.cover:
            print('Cover: %d lines' % len(self.cover))
            cover_cc = gitutil.BuildEmailList(self.get('cover_cc', ''))
            all_ccs = itertools.chain(cover_cc, *self._generated_cc.values())
            for email in set(all_ccs) - to_set - cc_set:
                print('      Cc: ', email)
        if cmd:
            print('Git command: %s' % cmd)
Beispiel #3
0
    def DoChecks(self):
        """Check that each version has a change log

        Print an error if something is wrong.
        """
        col = terminal.Color()
        if self.get('version'):
            changes_copy = dict(self.changes)
            for version in range(2, int(self.version) + 1):
                if self.changes.get(version):
                    del changes_copy[version]
                else:
                    str = 'Change log missing for v%d' % version
                    print col.Color(col.RED, str)
            for version in changes_copy:
                str = 'Change log for unknown version v%d' % version
                print col.Color(col.RED, str)
        elif self.changes:
            str = 'Change log exists, but no version is set'
            print col.Color(col.RED, str)
Beispiel #4
0
    def ShowActions(self, args, cmd, process_tags):
        """Show what actions we will/would perform

        Args:
            args: List of patch files we created
            cmd: The git command we would have run
            process_tags: Process tags as if they were aliases
        """
        col = terminal.Color()
        print 'Dry run, so not doing much. But I would do this:'
        print
        print 'Send a total of %d patch%s with %scover letter.' % (
            len(args), '' if len(args) == 1 else 'es',
            self.get('cover') and 'a ' or 'no ')

        # TODO: Colour the patches according to whether they passed checks
        for upto in range(len(args)):
            commit = self.commits[upto]
            print col.Color(col.GREEN, '   %s' % args[upto])
            cc_list = []
            if process_tags:
                cc_list += gitutil.BuildEmailList(commit.tags)
            cc_list += gitutil.BuildEmailList(commit.cc_list)

            for email in cc_list:
                if email == None:
                    email = col.Color(col.YELLOW,
                                      "<alias '%s' not found>" % tag)
                if email:
                    print '      Cc: ', email
        print
        for item in gitutil.BuildEmailList(self.get('to', '<none>')):
            print 'To:\t ', item
        for item in gitutil.BuildEmailList(self.cc):
            print 'Cc:\t ', item
        print 'Version: ', self.get('version')
        print 'Prefix:\t ', self.get('prefix')
        if self.cover:
            print 'Cover: %d lines' % len(self.cover)
        if cmd:
            print 'Git command: %s' % cmd
def ApplyPatch(verbose, fname):
    """Apply a patch with git am to test it

    TODO: Convert these to use command, with stderr option

    Args:
        fname: filename of patch file to apply
    """
    col = terminal.Color()
    cmd = ['git', 'am', fname]
    pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
            stderr=subprocess.PIPE)
    stdout, stderr = pipe.communicate()
    re_error = re.compile('^error: patch failed: (.+):(\d+)')
    for line in stderr.splitlines():
        if verbose:
            print line
        match = re_error.match(line)
        if match:
            print checkpatch.GetWarningMsg(col, 'warning', match.group(1),
                                           int(match.group(2)), 'Patch failed')
    return pipe.returncode == 0, stdout
Beispiel #6
0
    def setUp(self):
        # Set up commits to build
        self.commits = []
        sequence = 0
        for commit_info in commits:
            comm = commit.Commit(commit_info[0])
            comm.subject = commit_info[1]
            comm.return_code = commit_info[2]
            comm.error_list = commit_info[3]
            if sequence < 6:
                comm.error_list += [migration]
            comm.sequence = sequence
            sequence += 1
            self.commits.append(comm)

        # Set up boards to build
        self.boards = board.Boards()
        for brd in boards:
            self.boards.AddBoard(board.Board(*brd))
        self.boards.SelectBoards([])

        # Add some test settings
        bsettings.Setup(None)
        bsettings.AddFile(settings_data)

        # Set up the toolchains
        self.toolchains = toolchain.Toolchains()
        self.toolchains.Add('arm-linux-gcc', test=False)
        self.toolchains.Add('sparc-linux-gcc', test=False)
        self.toolchains.Add('powerpc-linux-gcc', test=False)
        self.toolchains.Add('gcc', test=False)

        # Avoid sending any output
        terminal.SetPrintTestMode()
        self._col = terminal.Color()

        self.base_dir = tempfile.mkdtemp()
        if not os.path.isdir(self.base_dir):
            os.mkdir(self.base_dir)
Beispiel #7
0
def CheckPatches(verbose, args):
    '''Run the checkpatch.pl script on each patch'''
    error_count, warning_count, check_count = 0, 0, 0
    col = terminal.Color()

    for fname in args:
        result = CheckPatch(fname, verbose)
        if not result.ok:
            error_count += result.errors
            warning_count += result.warnings
            check_count += result.checks
            print('%d errors, %d warnings, %d checks for %s:' %
                  (result.errors, result.warnings, result.checks,
                   col.Color(col.BLUE, fname)))
            if (len(result.problems) !=
                    result.errors + result.warnings + result.checks):
                print("Internal error: some problems lost")
            for item in result.problems:
                sys.stderr.write(
                    GetWarningMsg(col, item.get('type', '<unknown>'),
                                  item.get('file', '<unknown>'),
                                  item.get('line', 0),
                                  item.get('msg', 'message')))
            print
            #print(stdout)
    if error_count or warning_count or check_count:
        str = 'checkpatch.pl found %d error(s), %d warning(s), %d checks(s)'
        color = col.GREEN
        if warning_count:
            color = col.YELLOW
        if error_count:
            color = col.RED
        print(col.Color(color,
                        str % (error_count, warning_count, check_count)))
        return False
    return True
Beispiel #8
0
def LookupEmail(lookup_name, alias=None, raise_on_error=True, level=0):
    """If an email address is an alias, look it up and return the full name

    TODO: Why not just use git's own alias feature?

    Args:
        lookup_name: Alias or email address to look up
        alias: Dictionary containing aliases (None to use settings default)
        raise_on_error: True to raise an error when an alias fails to match,
                False to just print a message.

    Returns:
        tuple:
            list containing a list of email addresses

    Raises:
        OSError if a recursive alias reference was found
        ValueError if an alias was not found

    >>> alias = {}
    >>> alias['fred'] = ['*****@*****.**']
    >>> alias['john'] = ['*****@*****.**']
    >>> alias['mary'] = ['*****@*****.**']
    >>> alias['boys'] = ['fred', ' john', '*****@*****.**']
    >>> alias['all'] = ['fred ', 'john', '   mary   ']
    >>> alias['loop'] = ['other', 'john', '   mary   ']
    >>> alias['other'] = ['loop', 'john', '   mary   ']
    >>> LookupEmail('mary', alias)
    ['*****@*****.**']
    >>> LookupEmail('*****@*****.**', alias)
    ['*****@*****.**']
    >>> LookupEmail('boys', alias)
    ['*****@*****.**', '*****@*****.**']
    >>> LookupEmail('all', alias)
    ['*****@*****.**', '*****@*****.**', '*****@*****.**']
    >>> LookupEmail('odd', alias)
    Traceback (most recent call last):
    ...
    ValueError: Alias 'odd' not found
    >>> LookupEmail('loop', alias)
    Traceback (most recent call last):
    ...
    OSError: Recursive email alias at 'other'
    >>> LookupEmail('odd', alias, raise_on_error=False)
    \033[1;31mAlias 'odd' not found\033[0m
    []
    >>> # In this case the loop part will effectively be ignored.
    >>> LookupEmail('loop', alias, raise_on_error=False)
    \033[1;31mRecursive email alias at 'other'\033[0m
    \033[1;31mRecursive email alias at 'john'\033[0m
    \033[1;31mRecursive email alias at 'mary'\033[0m
    ['*****@*****.**', '*****@*****.**']
    """
    if not alias:
        alias = settings.alias
    lookup_name = lookup_name.strip()
    if '@' in lookup_name:  # Perhaps a real email address
        return [lookup_name]

    lookup_name = lookup_name.lower()
    col = terminal.Color()

    out_list = []
    if level > 10:
        msg = "Recursive email alias at '%s'" % lookup_name
        if raise_on_error:
            raise OSError, msg
        else:
            print col.Color(col.RED, msg)
            return out_list

    if lookup_name:
        if not lookup_name in alias:
            msg = "Alias '%s' not found" % lookup_name
            if raise_on_error:
                raise ValueError, msg
            else:
                print col.Color(col.RED, msg)
                return out_list
        for item in alias[lookup_name]:
            todo = LookupEmail(item, alias, raise_on_error, level + 1)
            for new_item in todo:
                if not new_item in out_list:
                    out_list.append(new_item)

    #print "No match for alias '%s'" % lookup_name
    return out_list
Beispiel #9
0
def DoBuildman(options, args):
    """The main control code for buildman

    Args:
        options: Command line options object
        args: Command line arguments (list of strings)
    """
    gitutil.Setup()

    bsettings.Setup()
    options.git_dir = os.path.join(options.git, '.git')

    toolchains = toolchain.Toolchains()
    toolchains.Scan(options.list_tool_chains)
    if options.list_tool_chains:
        toolchains.List()
        print
        return

    # Work out how many commits to build. We want to build everything on the
    # branch. We also build the upstream commit as a control so we can see
    # problems introduced by the first commit on the branch.
    col = terminal.Color()
    count = options.count
    if count == -1:
        if not options.branch:
            str = 'Please use -b to specify a branch to build'
            print col.Color(col.RED, str)
            sys.exit(1)
        count = gitutil.CountCommitsInBranch(options.git_dir, options.branch)
        if count is None:
            str = "Branch '%s' not found or has no upstream" % options.branch
            print col.Color(col.RED, str)
            sys.exit(1)
        count += 1  # Build upstream commit also

    if not count:
        str = ("No commits found to process in branch '%s': "
               "set branch's upstream or use -c flag" % options.branch)
        print col.Color(col.RED, str)
        sys.exit(1)

    # Work out what subset of the boards we are building
    boards = board.Boards()
    boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
    why_selected = boards.SelectBoards(args)
    selected = boards.GetSelected()
    if not len(selected):
        print col.Color(col.RED, 'No matching boards found')
        sys.exit(1)

    # Read the metadata from the commits. First look at the upstream commit,
    # then the ones in the branch. We would like to do something like
    # upstream/master~..branch but that isn't possible if upstream/master is
    # a merge commit (it will list all the commits that form part of the
    # merge)
    range_expr = gitutil.GetRangeInBranch(options.git_dir, options.branch)
    upstream_commit = gitutil.GetUpstream(options.git_dir, options.branch)
    series = patchstream.GetMetaDataForList(upstream_commit, options.git_dir,
                                            1)
    # Conflicting tags are not a problem for buildman, since it does not use
    # them. For example, Series-version is not useful for buildman. On the
    # other hand conflicting tags will cause an error. So allow later tags
    # to overwrite earlier ones.
    series.allow_overwrite = True
    series = patchstream.GetMetaDataForList(range_expr, options.git_dir, None,
                                            series)

    # By default we have one thread per CPU. But if there are not enough jobs
    # we can have fewer threads and use a high '-j' value for make.
    if not options.threads:
        options.threads = min(multiprocessing.cpu_count(), len(selected))
    if not options.jobs:
        options.jobs = max(1,
                           (multiprocessing.cpu_count() + len(selected) - 1) /
                           len(selected))

    if not options.step:
        options.step = len(series.commits) - 1

    # Create a new builder with the selected options
    output_dir = os.path.join('..', options.branch)
    builder = Builder(toolchains,
                      output_dir,
                      options.git_dir,
                      options.threads,
                      options.jobs,
                      checkout=True,
                      show_unknown=options.show_unknown,
                      step=options.step)
    builder.force_config_on_failure = not options.quick

    # For a dry run, just show our actions as a sanity check
    if options.dry_run:
        ShowActions(series, why_selected, selected, builder, options)
    else:
        builder.force_build = options.force_build

        # Work out which boards to build
        board_selected = boards.GetSelectedDict()

        print GetActionSummary(options.summary, count, board_selected, options)

        if options.summary:
            # We can't show function sizes without board details at present
            if options.show_bloat:
                options.show_detail = True
            builder.ShowSummary(series.commits, board_selected,
                                options.show_errors, options.show_sizes,
                                options.show_detail, options.show_bloat)
        else:
            builder.BuildBoards(series.commits, board_selected,
                                options.show_errors, options.keep_outputs)
Beispiel #10
0
    def testOutput(self):
        """Test basic builder operation and output

        This does a line-by-line verification of the summary output.
        """
        global base_dir

        base_dir = tempfile.mkdtemp()
        if not os.path.isdir(base_dir):
            os.mkdir(base_dir)
        build = builder.Builder(self.toolchains,
                                base_dir,
                                None,
                                1,
                                2,
                                checkout=False,
                                show_unknown=False)
        build.do_make = self.Make
        board_selected = self.boards.GetSelectedDict()

        # Build the boards for the pre-defined commits and warnings/errors
        # associated with each. This calls our Make() to inject the fake output.
        build.BuildBoards(self.commits,
                          board_selected,
                          keep_outputs=False,
                          verbose=False)
        lines = terminal.GetPrintTestLines()
        count = 0
        for line in lines:
            if line.text.strip():
                count += 1

        # We should get two starting messages, then an update for every commit
        # built.
        self.assertEqual(count, len(commits) * len(boards) + 2)
        build.SetDisplayOptions(show_errors=True)
        build.ShowSummary(self.commits, board_selected)
        #terminal.EchoPrintTestLines()
        lines = terminal.GetPrintTestLines()

        # Upstream commit: no errors
        self.assertEqual(lines[0].text, '01: %s' % commits[0][1])

        # Second commit: all archs should fail with warnings
        self.assertEqual(lines[1].text, '02: %s' % commits[1][1])

        col = terminal.Color()
        self.assertSummary(lines[2].text,
                           'arm',
                           'w+', ['board1'],
                           outcome=OUTCOME_WARN)
        self.assertSummary(lines[3].text,
                           'powerpc',
                           'w+', ['board2', 'board3'],
                           outcome=OUTCOME_WARN)
        self.assertSummary(lines[4].text,
                           'sandbox',
                           'w+', ['board4'],
                           outcome=OUTCOME_WARN)

        # Second commit: The warnings should be listed
        self.assertEqual(lines[5].text,
                         'w+%s' % errors[0].rstrip().replace('\n', '\nw+'))
        self.assertEqual(lines[5].colour, col.MAGENTA)

        # Third commit: Still fails
        self.assertEqual(lines[6].text, '03: %s' % commits[2][1])
        self.assertSummary(lines[7].text,
                           'arm',
                           '', ['board1'],
                           outcome=OUTCOME_OK)
        self.assertSummary(lines[8].text, 'powerpc', '+', ['board2', 'board3'])
        self.assertSummary(lines[9].text, 'sandbox', '+', ['board4'])

        # Expect a compiler error
        self.assertEqual(lines[10].text,
                         '+%s' % errors[1].rstrip().replace('\n', '\n+'))

        # Fourth commit: Compile errors are fixed, just have warning for board3
        self.assertEqual(lines[11].text, '04: %s' % commits[3][1])
        expect = '%10s: ' % 'powerpc'
        expect += ' ' + col.Color(col.GREEN, '')
        expect += '  '
        expect += col.Color(col.GREEN, ' %s' % 'board2')
        expect += ' ' + col.Color(col.YELLOW, 'w+')
        expect += '  '
        expect += col.Color(col.YELLOW, ' %s' % 'board3')
        self.assertEqual(lines[12].text, expect)
        self.assertSummary(lines[13].text,
                           'sandbox',
                           'w+', ['board4'],
                           outcome=OUTCOME_WARN)

        # Compile error fixed
        self.assertEqual(lines[14].text,
                         '-%s' % errors[1].rstrip().replace('\n', '\n-'))
        self.assertEqual(lines[14].colour, col.GREEN)

        self.assertEqual(lines[15].text,
                         'w+%s' % errors[2].rstrip().replace('\n', '\nw+'))
        self.assertEqual(lines[15].colour, col.MAGENTA)

        # Fifth commit
        self.assertEqual(lines[16].text, '05: %s' % commits[4][1])
        self.assertSummary(lines[17].text,
                           'powerpc',
                           '', ['board3'],
                           outcome=OUTCOME_OK)
        self.assertSummary(lines[18].text, 'sandbox', '+', ['board4'])

        # The second line of errors[3] is a duplicate, so buildman will drop it
        expect = errors[3].rstrip().split('\n')
        expect = [expect[0]] + expect[2:]
        self.assertEqual(lines[19].text,
                         '+%s' % '\n'.join(expect).replace('\n', '\n+'))

        self.assertEqual(lines[20].text,
                         'w-%s' % errors[2].rstrip().replace('\n', '\nw-'))

        # Sixth commit
        self.assertEqual(lines[21].text, '06: %s' % commits[5][1])
        self.assertSummary(lines[22].text,
                           'sandbox',
                           '', ['board4'],
                           outcome=OUTCOME_OK)

        # The second line of errors[3] is a duplicate, so buildman will drop it
        expect = errors[3].rstrip().split('\n')
        expect = [expect[0]] + expect[2:]
        self.assertEqual(lines[23].text,
                         '-%s' % '\n'.join(expect).replace('\n', '\n-'))

        self.assertEqual(lines[24].text,
                         'w-%s' % errors[0].rstrip().replace('\n', '\nw-'))

        # Seventh commit
        self.assertEqual(lines[25].text, '07: %s' % commits[6][1])
        self.assertSummary(lines[26].text, 'sandbox', '+', ['board4'])

        # Pick out the correct error lines
        expect_str = errors[4].rstrip().replace('%(basedir)s', '').split('\n')
        expect = expect_str[3:8] + [expect_str[-1]]
        self.assertEqual(lines[27].text,
                         '+%s' % '\n'.join(expect).replace('\n', '\n+'))

        # Now the warnings lines
        expect = [expect_str[0]] + expect_str[10:12] + [expect_str[9]]
        self.assertEqual(lines[28].text,
                         'w+%s' % '\n'.join(expect).replace('\n', '\nw+'))

        self.assertEqual(len(lines), 29)
        shutil.rmtree(base_dir)
Beispiel #11
0
def ApplyPatches(verbose, args, start_point):
    """Apply the patches with git am to make sure all is well

    Args:
        verbose: Print out 'git am' output verbatim
        args: List of patch files to apply
        start_point: Number of commits back from HEAD to start applying.
            Normally this is len(args), but it can be larger if a start
            offset was given.
    """
    error_count = 0
    col = terminal.Color()

    # Figure out our current position
    cmd = ['git', 'name-rev', 'HEAD', '--name-only']
    pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    stdout, stderr = pipe.communicate()
    if pipe.returncode:
        str = 'Could not find current commit name'
        print col.Color(col.RED, str)
        print stdout
        return False
    old_head = stdout.splitlines()[0]
    if old_head == 'undefined':
        str = "Invalid HEAD '%s'" % stdout.strip()
        print col.Color(col.RED, str)
        return False

    # Checkout the required start point
    cmd = ['git', 'checkout', 'HEAD~%d' % start_point]
    pipe = subprocess.Popen(cmd,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    stdout, stderr = pipe.communicate()
    if pipe.returncode:
        str = 'Could not move to commit before patch series'
        print col.Color(col.RED, str)
        print stdout, stderr
        return False

    # Apply all the patches
    for fname in args:
        ok, stdout = ApplyPatch(verbose, fname)
        if not ok:
            print col.Color(
                col.RED, 'git am returned errors for %s: will '
                'skip this patch' % fname)
            if verbose:
                print stdout
            error_count += 1
            cmd = ['git', 'am', '--skip']
            pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE)
            stdout, stderr = pipe.communicate()
            if pipe.returncode != 0:
                print col.Color(col.RED, 'Unable to skip patch! Aborting...')
                print stdout
                break

    # Return to our previous position
    cmd = ['git', 'checkout', old_head]
    pipe = subprocess.Popen(cmd,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    stdout, stderr = pipe.communicate()
    if pipe.returncode:
        print col.Color(col.RED, 'Could not move back to head commit')
        print stdout, stderr
    return error_count == 0
Beispiel #12
0
    def testOutput(self):
        """Test basic builder operation and output

        This does a line-by-line verification of the summary output.
        """
        global base_dir

        base_dir = tempfile.mkdtemp()
        if not os.path.isdir(base_dir):
            os.mkdir(base_dir)
        build = builder.Builder(self.toolchains,
                                base_dir,
                                None,
                                1,
                                2,
                                checkout=False,
                                show_unknown=False)
        build.do_make = self.Make
        board_selected = self.boards.GetSelectedDict()

        build.BuildBoards(self.commits,
                          board_selected,
                          keep_outputs=False,
                          verbose=False)
        lines = terminal.GetPrintTestLines()
        count = 0
        for line in lines:
            if line.text.strip():
                count += 1

        # We should get one starting message, then an update for every commit
        # built.
        self.assertEqual(count, len(commits) * len(boards) + 1)
        build.SetDisplayOptions(show_errors=True)
        build.ShowSummary(self.commits, board_selected)
        #terminal.EchoPrintTestLines()
        lines = terminal.GetPrintTestLines()
        self.assertEqual(lines[0].text, '01: %s' % commits[0][1])
        self.assertEqual(lines[1].text, '02: %s' % commits[1][1])

        # We expect all archs to fail
        col = terminal.Color()
        self.assertSummary(lines[2].text, 'sandbox', '+', ['board4'])
        self.assertSummary(lines[3].text, 'arm', '+', ['board1'])
        self.assertSummary(lines[4].text, 'powerpc', '+', ['board2', 'board3'])

        # Now we should have the compiler warning
        self.assertEqual(lines[5].text,
                         'w+%s' % errors[0].rstrip().replace('\n', '\nw+'))
        self.assertEqual(lines[5].colour, col.MAGENTA)

        self.assertEqual(lines[6].text, '03: %s' % commits[2][1])
        self.assertSummary(lines[7].text, 'sandbox', '+', ['board4'])
        self.assertSummary(lines[8].text, 'arm', '', ['board1'], ok=True)
        self.assertSummary(lines[9].text, 'powerpc', '+', ['board2', 'board3'])

        # Compiler error
        self.assertEqual(lines[10].text,
                         '+%s' % errors[1].rstrip().replace('\n', '\n+'))

        self.assertEqual(lines[11].text, '04: %s' % commits[3][1])
        self.assertSummary(lines[12].text, 'sandbox', '', ['board4'], ok=True)
        self.assertSummary(lines[13].text,
                           'powerpc',
                           '', ['board2', 'board3'],
                           ok=True)

        # Compile error fixed
        self.assertEqual(lines[14].text,
                         '-%s' % errors[1].rstrip().replace('\n', '\n-'))
        self.assertEqual(lines[14].colour, col.GREEN)

        self.assertEqual(lines[15].text,
                         'w+%s' % errors[2].rstrip().replace('\n', '\nw+'))
        self.assertEqual(lines[15].colour, col.MAGENTA)

        self.assertEqual(lines[16].text, '05: %s' % commits[4][1])
        self.assertSummary(lines[17].text, 'sandbox', '+', ['board4'])
        self.assertSummary(lines[18].text, 'powerpc', '', ['board3'], ok=True)

        # The second line of errors[3] is a duplicate, so buildman will drop it
        expect = errors[3].rstrip().split('\n')
        expect = [expect[0]] + expect[2:]
        self.assertEqual(lines[19].text,
                         '+%s' % '\n'.join(expect).replace('\n', '\n+'))

        self.assertEqual(lines[20].text,
                         'w-%s' % errors[2].rstrip().replace('\n', '\nw-'))

        self.assertEqual(lines[21].text, '06: %s' % commits[5][1])
        self.assertSummary(lines[22].text, 'sandbox', '', ['board4'], ok=True)

        # The second line of errors[3] is a duplicate, so buildman will drop it
        expect = errors[3].rstrip().split('\n')
        expect = [expect[0]] + expect[2:]
        self.assertEqual(lines[23].text,
                         '-%s' % '\n'.join(expect).replace('\n', '\n-'))

        self.assertEqual(lines[24].text,
                         'w-%s' % errors[0].rstrip().replace('\n', '\nw-'))

        self.assertEqual(lines[25].text, '07: %s' % commits[6][1])
        self.assertSummary(lines[26].text, 'sandbox', '+', ['board4'])

        # Pick out the correct error lines
        expect_str = errors[4].rstrip().replace('%(basedir)s', '').split('\n')
        expect = expect_str[3:8] + [expect_str[-1]]
        self.assertEqual(lines[27].text,
                         '+%s' % '\n'.join(expect).replace('\n', '\n+'))

        # Now the warnings lines
        expect = [expect_str[0]] + expect_str[10:12] + [expect_str[9]]
        self.assertEqual(lines[28].text,
                         'w+%s' % '\n'.join(expect).replace('\n', '\nw+'))

        self.assertEqual(len(lines), 29)
        shutil.rmtree(base_dir)
Beispiel #13
0
    def __init__(self,
                 toolchains,
                 base_dir,
                 git_dir,
                 num_threads,
                 num_jobs,
                 gnu_make='make',
                 checkout=True,
                 show_unknown=True,
                 step=1,
                 no_subdirs=False,
                 full_path=False,
                 verbose_build=False):
        """Create a new Builder object

        Args:
            toolchains: Toolchains object to use for building
            base_dir: Base directory to use for builder
            git_dir: Git directory containing source repository
            num_threads: Number of builder threads to run
            num_jobs: Number of jobs to run at once (passed to make as -j)
            gnu_make: the command name of GNU Make.
            checkout: True to check out source, False to skip that step.
                This is used for testing.
            show_unknown: Show unknown boards (those not built) in summary
            step: 1 to process every commit, n to process every nth commit
            no_subdirs: Don't create subdirectories when building current
                source for a single board
            full_path: Return the full path in CROSS_COMPILE and don't set
                PATH
            verbose_build: Run build with V=1 and don't use 'make -s'
        """
        self.toolchains = toolchains
        self.base_dir = base_dir
        self._working_dir = os.path.join(base_dir, '.bm-work')
        self.threads = []
        self.active = True
        self.do_make = self.Make
        self.gnu_make = gnu_make
        self.checkout = checkout
        self.num_threads = num_threads
        self.num_jobs = num_jobs
        self.already_done = 0
        self.force_build = False
        self.git_dir = git_dir
        self._show_unknown = show_unknown
        self._timestamp_count = 10
        self._build_period_us = None
        self._complete_delay = None
        self._next_delay_update = datetime.now()
        self.force_config_on_failure = True
        self.force_build_failures = False
        self.force_reconfig = False
        self._step = step
        self.in_tree = False
        self._error_lines = 0
        self.no_subdirs = no_subdirs
        self.full_path = full_path
        self.verbose_build = verbose_build

        self.col = terminal.Color()

        self._re_function = re.compile('(.*): In function.*')
        self._re_files = re.compile('In file included from.*')
        self._re_warning = re.compile('(.*):(\d*):(\d*): warning: .*')
        self._re_note = re.compile(
            '(.*):(\d*):(\d*): note: this is the location of the previous.*')

        self.queue = Queue.Queue()
        self.out_queue = Queue.Queue()
        for i in range(self.num_threads):
            t = builderthread.BuilderThread(self, i)
            t.setDaemon(True)
            t.start()
            self.threads.append(t)

        self.last_line_len = 0
        t = builderthread.ResultThread(self)
        t.setDaemon(True)
        t.start()
        self.threads.append(t)

        ignore_lines = [
            '(make.*Waiting for unfinished)', '(Segmentation fault)'
        ]
        self.re_make_err = re.compile('|'.join(ignore_lines))
Beispiel #14
0
elif options.full_help:
    pager = os.getenv('PAGER')
    if not pager:
        pager = 'more'
    fname = os.path.join(os.path.dirname(sys.argv[0]), 'README')
    command.Run(pager, fname)

# Process commits, produce patches files, check them, email them
else:
    gitutil.Setup()

    if options.count == -1:
        # Work out how many patches to send if we can
        options.count = gitutil.CountCommitsToBranch() - options.start

    col = terminal.Color()
    if not options.count:
        str = 'No commits found to process - please use -c flag'
        sys.exit(col.Color(col.RED, str))

    # Read the metadata from the commits
    if options.count:
        series = patchstream.GetMetaData(options.start, options.count)
        cover_fname, args = gitutil.CreatePatches(options.start, options.count,
                                                  series)

    # Fix up the patch files to our liking, and insert the cover letter
    series = patchstream.FixPatches(series, args)
    if series and cover_fname and series.get('cover'):
        patchstream.InsertCoverLetter(cover_fname, series, options.count)
Beispiel #15
0
def DoBuildman(options,
               args,
               toolchains=None,
               make_func=None,
               boards=None,
               clean_dir=False):
    """The main control code for buildman

    Args:
        options: Command line options object
        args: Command line arguments (list of strings)
        toolchains: Toolchains to use - this should be a Toolchains()
                object. If None, then it will be created and scanned
        make_func: Make function to use for the builder. This is called
                to execute 'make'. If this is None, the normal function
                will be used, which calls the 'make' tool with suitable
                arguments. This setting is useful for tests.
        board: Boards() object to use, containing a list of available
                boards. If this is None it will be created and scanned.
    """
    global builder

    if options.full_help:
        pager = os.getenv('PAGER')
        if not pager:
            pager = 'more'
        fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
                             'README')
        command.Run(pager, fname)
        return 0

    gitutil.Setup()
    col = terminal.Color()

    options.git_dir = os.path.join(options.git, '.git')

    no_toolchains = toolchains is None
    if no_toolchains:
        toolchains = toolchain.Toolchains(options.override_toolchain)

    if options.fetch_arch:
        if options.fetch_arch == 'list':
            sorted_list = toolchains.ListArchs()
            print col.Color(
                col.BLUE,
                'Available architectures: %s\n' % ' '.join(sorted_list))
            return 0
        else:
            fetch_arch = options.fetch_arch
            if fetch_arch == 'all':
                fetch_arch = ','.join(toolchains.ListArchs())
                print col.Color(col.CYAN,
                                '\nDownloading toolchains: %s' % fetch_arch)
            for arch in fetch_arch.split(','):
                print
                ret = toolchains.FetchAndInstall(arch)
                if ret:
                    return ret
            return 0

    if no_toolchains:
        toolchains.GetSettings()
        toolchains.Scan(options.list_tool_chains and options.verbose)
    if options.list_tool_chains:
        toolchains.List()
        print
        return 0

    # Work out how many commits to build. We want to build everything on the
    # branch. We also build the upstream commit as a control so we can see
    # problems introduced by the first commit on the branch.
    count = options.count
    has_range = options.branch and '..' in options.branch
    if count == -1:
        if not options.branch:
            count = 1
        else:
            if has_range:
                count, msg = gitutil.CountCommitsInRange(
                    options.git_dir, options.branch)
            else:
                count, msg = gitutil.CountCommitsInBranch(
                    options.git_dir, options.branch)
            if count is None:
                sys.exit(col.Color(col.RED, msg))
            elif count == 0:
                sys.exit(
                    col.Color(col.RED,
                              "Range '%s' has no commits" % options.branch))
            if msg:
                print col.Color(col.YELLOW, msg)
            count += 1  # Build upstream commit also

    if not count:
        str = ("No commits found to process in branch '%s': "
               "set branch's upstream or use -c flag" % options.branch)
        sys.exit(col.Color(col.RED, str))

    # Work out what subset of the boards we are building
    if not boards:
        board_file = os.path.join(options.output_dir, 'boards.cfg')
        genboardscfg = os.path.join(options.git, 'tools/genboardscfg.py')
        status = subprocess.call([genboardscfg, '-o', board_file])
        if status != 0:
            sys.exit("Failed to generate boards.cfg")

        boards = board.Boards()
        boards.ReadBoards(board_file)

    exclude = []
    if options.exclude:
        for arg in options.exclude:
            exclude += arg.split(',')

    if options.boards:
        requested_boards = []
        for b in options.boards:
            requested_boards += b.split(',')
    else:
        requested_boards = None
    why_selected, board_warnings = boards.SelectBoards(args, exclude,
                                                       requested_boards)
    selected = boards.GetSelected()
    if not len(selected):
        sys.exit(col.Color(col.RED, 'No matching boards found'))

    # Read the metadata from the commits. First look at the upstream commit,
    # then the ones in the branch. We would like to do something like
    # upstream/master~..branch but that isn't possible if upstream/master is
    # a merge commit (it will list all the commits that form part of the
    # merge)
    # Conflicting tags are not a problem for buildman, since it does not use
    # them. For example, Series-version is not useful for buildman. On the
    # other hand conflicting tags will cause an error. So allow later tags
    # to overwrite earlier ones by setting allow_overwrite=True
    if options.branch:
        if count == -1:
            if has_range:
                range_expr = options.branch
            else:
                range_expr = gitutil.GetRangeInBranch(options.git_dir,
                                                      options.branch)
            upstream_commit = gitutil.GetUpstream(options.git_dir,
                                                  options.branch)
            series = patchstream.GetMetaDataForList(upstream_commit,
                                                    options.git_dir,
                                                    1,
                                                    series=None,
                                                    allow_overwrite=True)

            series = patchstream.GetMetaDataForList(range_expr,
                                                    options.git_dir,
                                                    None,
                                                    series,
                                                    allow_overwrite=True)
        else:
            # Honour the count
            series = patchstream.GetMetaDataForList(options.branch,
                                                    options.git_dir,
                                                    count,
                                                    series=None,
                                                    allow_overwrite=True)
    else:
        series = None
        if not options.dry_run:
            options.verbose = True
            if not options.summary:
                options.show_errors = True

    # By default we have one thread per CPU. But if there are not enough jobs
    # we can have fewer threads and use a high '-j' value for make.
    if not options.threads:
        options.threads = min(multiprocessing.cpu_count(), len(selected))
    if not options.jobs:
        options.jobs = max(1,
                           (multiprocessing.cpu_count() + len(selected) - 1) /
                           len(selected))

    if not options.step:
        options.step = len(series.commits) - 1

    gnu_make = command.Output(os.path.join(options.git,
                                           'scripts/show-gnu-make'),
                              raise_on_error=False).rstrip()
    if not gnu_make:
        sys.exit('GNU Make not found')

    # Create a new builder with the selected options.
    output_dir = options.output_dir
    if options.branch:
        dirname = options.branch.replace('/', '_')
        # As a special case allow the board directory to be placed in the
        # output directory itself rather than any subdirectory.
        if not options.no_subdirs:
            output_dir = os.path.join(options.output_dir, dirname)
        if clean_dir and os.path.exists(output_dir):
            shutil.rmtree(output_dir)
    CheckOutputDir(output_dir)
    builder = Builder(toolchains,
                      output_dir,
                      options.git_dir,
                      options.threads,
                      options.jobs,
                      gnu_make=gnu_make,
                      checkout=True,
                      show_unknown=options.show_unknown,
                      step=options.step,
                      no_subdirs=options.no_subdirs,
                      full_path=options.full_path,
                      verbose_build=options.verbose_build,
                      incremental=options.incremental,
                      per_board_out_dir=options.per_board_out_dir,
                      config_only=options.config_only,
                      squash_config_y=not options.preserve_config_y,
                      warnings_as_errors=options.warnings_as_errors)
    builder.force_config_on_failure = not options.quick
    if make_func:
        builder.do_make = make_func

    # For a dry run, just show our actions as a sanity check
    if options.dry_run:
        ShowActions(series, why_selected, selected, builder, options,
                    board_warnings)
    else:
        builder.force_build = options.force_build
        builder.force_build_failures = options.force_build_failures
        builder.force_reconfig = options.force_reconfig
        builder.in_tree = options.in_tree

        # Work out which boards to build
        board_selected = boards.GetSelectedDict()

        if series:
            commits = series.commits
            # Number the commits for test purposes
            for commit in range(len(commits)):
                commits[commit].sequence = commit
        else:
            commits = None

        Print(
            GetActionSummary(options.summary, commits, board_selected,
                             options))

        # We can't show function sizes without board details at present
        if options.show_bloat:
            options.show_detail = True
        builder.SetDisplayOptions(options.show_errors, options.show_sizes,
                                  options.show_detail, options.show_bloat,
                                  options.list_error_boards,
                                  options.show_config,
                                  options.show_environment)
        if options.summary:
            builder.ShowSummary(commits, board_selected)
        else:
            fail, warned = builder.BuildBoards(commits, board_selected,
                                               options.keep_outputs,
                                               options.verbose)
            if fail:
                return 128
            elif warned:
                return 129
    return 0
Beispiel #16
0
    def __init__(self,
                 toolchains,
                 base_dir,
                 git_dir,
                 num_threads,
                 num_jobs,
                 gnu_make='make',
                 checkout=True,
                 show_unknown=True,
                 step=1):
        """Create a new Builder object

        Args:
            toolchains: Toolchains object to use for building
            base_dir: Base directory to use for builder
            git_dir: Git directory containing source repository
            num_threads: Number of builder threads to run
            num_jobs: Number of jobs to run at once (passed to make as -j)
            gnu_make: the command name of GNU Make.
            checkout: True to check out source, False to skip that step.
                This is used for testing.
            show_unknown: Show unknown boards (those not built) in summary
            step: 1 to process every commit, n to process every nth commit
        """
        self.toolchains = toolchains
        self.base_dir = base_dir
        self._working_dir = os.path.join(base_dir, '.bm-work')
        self.threads = []
        self.active = True
        self.do_make = self.Make
        self.gnu_make = gnu_make
        self.checkout = checkout
        self.num_threads = num_threads
        self.num_jobs = num_jobs
        self.already_done = 0
        self.force_build = False
        self.git_dir = git_dir
        self._show_unknown = show_unknown
        self._timestamp_count = 10
        self._build_period_us = None
        self._complete_delay = None
        self._next_delay_update = datetime.now()
        self.force_config_on_failure = True
        self.force_build_failures = False
        self.force_reconfig = False
        self._step = step
        self.in_tree = False
        self._error_lines = 0

        self.col = terminal.Color()

        self.queue = Queue.Queue()
        self.out_queue = Queue.Queue()
        for i in range(self.num_threads):
            t = builderthread.BuilderThread(self, i)
            t.setDaemon(True)
            t.start()
            self.threads.append(t)

        self.last_line_len = 0
        t = builderthread.ResultThread(self)
        t.setDaemon(True)
        t.start()
        self.threads.append(t)

        ignore_lines = [
            '(make.*Waiting for unfinished)', '(Segmentation fault)'
        ]
        self.re_make_err = re.compile('|'.join(ignore_lines))
Beispiel #17
0
 def test_property_raise(self):
     s = terminal.Color('text')
     print(s.unknown)
Beispiel #18
0
 def test_unknown_bg(self):
     s = terminal.Color('text')
     print(s.unknown_bg)
Beispiel #19
0
def DoBuildman(options, args):
    """The main control code for buildman

    Args:
        options: Command line options object
        args: Command line arguments (list of strings)
    """
    gitutil.Setup()

    bsettings.Setup(options.config_file)
    options.git_dir = os.path.join(options.git, '.git')

    toolchains = toolchain.Toolchains()
    toolchains.Scan(options.list_tool_chains)
    if options.list_tool_chains:
        toolchains.List()
        print
        return

    # Work out how many commits to build. We want to build everything on the
    # branch. We also build the upstream commit as a control so we can see
    # problems introduced by the first commit on the branch.
    col = terminal.Color()
    count = options.count
    if count == -1:
        if not options.branch:
            count = 1
        else:
            count = gitutil.CountCommitsInBranch(options.git_dir,
                                                 options.branch)
            if count is None:
                str = ("Branch '%s' not found or has no upstream" %
                       options.branch)
                print col.Color(col.RED, str)
                sys.exit(1)
            count += 1  # Build upstream commit also

    if not count:
        str = ("No commits found to process in branch '%s': "
               "set branch's upstream or use -c flag" % options.branch)
        print col.Color(col.RED, str)
        sys.exit(1)

    # Work out what subset of the boards we are building
    board_file = os.path.join(options.git, 'boards.cfg')
    if not os.path.exists(board_file):
        print 'Could not find %s' % board_file
        status = subprocess.call(
            [os.path.join(options.git, 'tools/genboardscfg.py')])
        if status != 0:
            print >> sys.stderr, "Failed to generate boards.cfg"
            sys.exit(1)

    boards = board.Boards()
    boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
    why_selected = boards.SelectBoards(args)
    selected = boards.GetSelected()
    if not len(selected):
        print col.Color(col.RED, 'No matching boards found')
        sys.exit(1)

    # Read the metadata from the commits. First look at the upstream commit,
    # then the ones in the branch. We would like to do something like
    # upstream/master~..branch but that isn't possible if upstream/master is
    # a merge commit (it will list all the commits that form part of the
    # merge)
    if options.branch:
        if count == -1:
            range_expr = gitutil.GetRangeInBranch(options.git_dir,
                                                  options.branch)
            upstream_commit = gitutil.GetUpstream(options.git_dir,
                                                  options.branch)
            series = patchstream.GetMetaDataForList(upstream_commit,
                                                    options.git_dir, 1)

            # Conflicting tags are not a problem for buildman, since it does
            # not use them. For example, Series-version is not useful for
            # buildman. On the other hand conflicting tags will cause an
            # error. So allow later tags to overwrite earlier ones.
            series.allow_overwrite = True
            series = patchstream.GetMetaDataForList(range_expr,
                                                    options.git_dir, None,
                                                    series)
        else:
            # Honour the count
            series = patchstream.GetMetaDataForList(options.branch,
                                                    options.git_dir, count)
    else:
        series = None
        options.verbose = True
        options.show_errors = True

    # By default we have one thread per CPU. But if there are not enough jobs
    # we can have fewer threads and use a high '-j' value for make.
    if not options.threads:
        options.threads = min(multiprocessing.cpu_count(), len(selected))
    if not options.jobs:
        options.jobs = max(1,
                           (multiprocessing.cpu_count() + len(selected) - 1) /
                           len(selected))

    if not options.step:
        options.step = len(series.commits) - 1

    gnu_make = command.Output(
        os.path.join(options.git, 'scripts/show-gnu-make')).rstrip()
    if not gnu_make:
        print >> sys.stderr, 'GNU Make not found'
        sys.exit(1)

    # Create a new builder with the selected options
    if options.branch:
        dirname = options.branch
    else:
        dirname = 'current'
    output_dir = os.path.join(options.output_dir, dirname)
    builder = Builder(toolchains,
                      output_dir,
                      options.git_dir,
                      options.threads,
                      options.jobs,
                      gnu_make=gnu_make,
                      checkout=True,
                      show_unknown=options.show_unknown,
                      step=options.step)
    builder.force_config_on_failure = not options.quick

    # For a dry run, just show our actions as a sanity check
    if options.dry_run:
        ShowActions(series, why_selected, selected, builder, options)
    else:
        builder.force_build = options.force_build
        builder.force_build_failures = options.force_build_failures
        builder.force_reconfig = options.force_reconfig
        builder.in_tree = options.in_tree

        # Work out which boards to build
        board_selected = boards.GetSelectedDict()

        if series:
            commits = series.commits
        else:
            commits = None

        print GetActionSummary(options.summary, commits, board_selected,
                               options)

        builder.SetDisplayOptions(options.show_errors, options.show_sizes,
                                  options.show_detail, options.show_bloat)
        if options.summary:
            # We can't show function sizes without board details at present
            if options.show_bloat:
                options.show_detail = True
            builder.ShowSummary(commits, board_selected)
        else:
            builder.BuildBoards(commits, board_selected, options.keep_outputs,
                                options.verbose)
Beispiel #20
0
 def test_radd_raise(self):
     foo = terminal.Color('foo')
     print(1 + foo.green)
Beispiel #21
0
 def test_repr(self):
     foo = terminal.Color('foo')
     foo.fgcolor = 'red'
     assert repr(foo) == repr(str(foo))
Beispiel #22
0
    def _CheckOutput(self,
                     lines,
                     list_error_boards=False,
                     filter_dtb_warnings=False,
                     filter_migration_warnings=False):
        """Check for expected output from the build summary

        Args:
            lines: Iterator containing the lines returned from the summary
            list_error_boards: Adjust the check for output produced with the
               --list-error-boards flag
            filter_dtb_warnings: Adjust the check for output produced with the
               --filter-dtb-warnings flag
        """
        def add_line_prefix(prefix, boards, error_str, colour):
            """Add a prefix to each line of a string

            The training \n in error_str is removed before processing

            Args:
                prefix: String prefix to add
                error_str: Error string containing the lines
                colour: Expected colour for the line. Note that the board list,
                    if present, always appears in magenta

            Returns:
                New string where each line has the prefix added
            """
            lines = error_str.strip().splitlines()
            new_lines = []
            for line in lines:
                if boards:
                    expect = self._col.Color(colour, prefix + '(')
                    expect += self._col.Color(self._col.MAGENTA,
                                              boards,
                                              bright=False)
                    expect += self._col.Color(colour, ') %s' % line)
                else:
                    expect = self._col.Color(colour, prefix + line)
                new_lines.append(expect)
            return '\n'.join(new_lines)

        col = terminal.Color()
        boards01234 = ('board0 board1 board2 board3 board4'
                       if list_error_boards else '')
        boards1234 = 'board1 board2 board3 board4' if list_error_boards else ''
        boards234 = 'board2 board3 board4' if list_error_boards else ''
        boards34 = 'board3 board4' if list_error_boards else ''
        boards4 = 'board4' if list_error_boards else ''

        # Upstream commit: migration warnings only
        self.assertEqual(next(lines).text, '01: %s' % commits[0][1])

        if not filter_migration_warnings:
            self.assertSummary(next(lines).text,
                               'arm',
                               'w+', ['board0', 'board1'],
                               outcome=OUTCOME_WARN)
            self.assertSummary(next(lines).text,
                               'powerpc',
                               'w+', ['board2', 'board3'],
                               outcome=OUTCOME_WARN)
            self.assertSummary(next(lines).text,
                               'sandbox',
                               'w+', ['board4'],
                               outcome=OUTCOME_WARN)

            self.assertEqual(
                next(lines).text,
                add_line_prefix('+', boards01234, migration, col.RED))

        # Second commit: all archs should fail with warnings
        self.assertEqual(next(lines).text, '02: %s' % commits[1][1])

        if filter_migration_warnings:
            self.assertSummary(next(lines).text,
                               'arm',
                               'w+', ['board1'],
                               outcome=OUTCOME_WARN)
            self.assertSummary(next(lines).text,
                               'powerpc',
                               'w+', ['board2', 'board3'],
                               outcome=OUTCOME_WARN)
            self.assertSummary(next(lines).text,
                               'sandbox',
                               'w+', ['board4'],
                               outcome=OUTCOME_WARN)

        # Second commit: The warnings should be listed
        self.assertEqual(
            next(lines).text,
            add_line_prefix('w+', boards1234, errors[0], col.YELLOW))

        # Third commit: Still fails
        self.assertEqual(next(lines).text, '03: %s' % commits[2][1])
        if filter_migration_warnings:
            self.assertSummary(next(lines).text,
                               'arm',
                               '', ['board1'],
                               outcome=OUTCOME_OK)
        self.assertSummary(
            next(lines).text, 'powerpc', '+', ['board2', 'board3'])
        self.assertSummary(next(lines).text, 'sandbox', '+', ['board4'])

        # Expect a compiler error
        self.assertEqual(
            next(lines).text,
            add_line_prefix('+', boards234, errors[1], col.RED))

        # Fourth commit: Compile errors are fixed, just have warning for board3
        self.assertEqual(next(lines).text, '04: %s' % commits[3][1])
        if filter_migration_warnings:
            expect = '%10s: ' % 'powerpc'
            expect += ' ' + col.Color(col.GREEN, '')
            expect += '  '
            expect += col.Color(col.GREEN, ' %s' % 'board2')
            expect += ' ' + col.Color(col.YELLOW, 'w+')
            expect += '  '
            expect += col.Color(col.YELLOW, ' %s' % 'board3')
            self.assertEqual(next(lines).text, expect)
        else:
            self.assertSummary(next(lines).text,
                               'powerpc',
                               'w+', ['board2', 'board3'],
                               outcome=OUTCOME_WARN)
        self.assertSummary(next(lines).text,
                           'sandbox',
                           'w+', ['board4'],
                           outcome=OUTCOME_WARN)

        # Compile error fixed
        self.assertEqual(
            next(lines).text,
            add_line_prefix('-', boards234, errors[1], col.GREEN))

        if not filter_dtb_warnings:
            self.assertEqual(
                next(lines).text,
                add_line_prefix('w+', boards34, errors[2], col.YELLOW))

        # Fifth commit
        self.assertEqual(next(lines).text, '05: %s' % commits[4][1])
        if filter_migration_warnings:
            self.assertSummary(next(lines).text,
                               'powerpc',
                               '', ['board3'],
                               outcome=OUTCOME_OK)
        self.assertSummary(next(lines).text, 'sandbox', '+', ['board4'])

        # The second line of errors[3] is a duplicate, so buildman will drop it
        expect = errors[3].rstrip().split('\n')
        expect = [expect[0]] + expect[2:]
        expect = '\n'.join(expect)
        self.assertEqual(
            next(lines).text, add_line_prefix('+', boards4, expect, col.RED))

        if not filter_dtb_warnings:
            self.assertEqual(
                next(lines).text,
                add_line_prefix('w-', boards34, errors[2], col.CYAN))

        # Sixth commit
        self.assertEqual(next(lines).text, '06: %s' % commits[5][1])
        if filter_migration_warnings:
            self.assertSummary(next(lines).text,
                               'sandbox',
                               '', ['board4'],
                               outcome=OUTCOME_OK)
        else:
            self.assertSummary(next(lines).text,
                               'sandbox',
                               'w+', ['board4'],
                               outcome=OUTCOME_WARN)

        # The second line of errors[3] is a duplicate, so buildman will drop it
        expect = errors[3].rstrip().split('\n')
        expect = [expect[0]] + expect[2:]
        expect = '\n'.join(expect)
        self.assertEqual(
            next(lines).text, add_line_prefix('-', boards4, expect, col.GREEN))
        self.assertEqual(
            next(lines).text,
            add_line_prefix('w-', boards4, errors[0], col.CYAN))

        # Seventh commit
        self.assertEqual(next(lines).text, '07: %s' % commits[6][1])
        if filter_migration_warnings:
            self.assertSummary(next(lines).text, 'sandbox', '+', ['board4'])
        else:
            self.assertSummary(next(lines).text,
                               'arm',
                               '', ['board0', 'board1'],
                               outcome=OUTCOME_OK)
            self.assertSummary(next(lines).text,
                               'powerpc',
                               '', ['board2', 'board3'],
                               outcome=OUTCOME_OK)
            self.assertSummary(next(lines).text, 'sandbox', '+', ['board4'])

        # Pick out the correct error lines
        expect_str = errors[4].rstrip().replace('%(basedir)s', '').split('\n')
        expect = expect_str[3:8] + [expect_str[-1]]
        expect = '\n'.join(expect)
        if not filter_migration_warnings:
            self.assertEqual(
                next(lines).text,
                add_line_prefix('-', boards01234, migration, col.GREEN))

        self.assertEqual(
            next(lines).text, add_line_prefix('+', boards4, expect, col.RED))

        # Now the warnings lines
        expect = [expect_str[0]] + expect_str[10:12] + [expect_str[9]]
        expect = '\n'.join(expect)
        self.assertEqual(
            next(lines).text, add_line_prefix('w+', boards4, expect,
                                              col.YELLOW))