xref: /rk3399_rockchip-uboot/tools/buildman/control.py (revision 48ba5856eb47dca0abc4d24e7c4e3ce1fd2628f1)
1fc3fe1c2SSimon Glass# Copyright (c) 2013 The Chromium OS Authors.
2fc3fe1c2SSimon Glass#
31a459660SWolfgang Denk# SPDX-License-Identifier:	GPL-2.0+
4fc3fe1c2SSimon Glass#
5fc3fe1c2SSimon Glass
6fc3fe1c2SSimon Glassimport multiprocessing
7fc3fe1c2SSimon Glassimport os
8fc3fe1c2SSimon Glassimport sys
9fc3fe1c2SSimon Glass
10fc3fe1c2SSimon Glassimport board
11fc3fe1c2SSimon Glassimport bsettings
12fc3fe1c2SSimon Glassfrom builder import Builder
13fc3fe1c2SSimon Glassimport gitutil
14fc3fe1c2SSimon Glassimport patchstream
15fc3fe1c2SSimon Glassimport terminal
16fc3fe1c2SSimon Glassimport toolchain
1799796923SMasahiro Yamadaimport command
1873f30b9bSMasahiro Yamadaimport subprocess
19fc3fe1c2SSimon Glass
20fc3fe1c2SSimon Glassdef GetPlural(count):
21fc3fe1c2SSimon Glass    """Returns a plural 's' if count is not 1"""
22fc3fe1c2SSimon Glass    return 's' if count != 1 else ''
23fc3fe1c2SSimon Glass
24fea5858eSSimon Glassdef GetActionSummary(is_summary, commits, selected, options):
25fc3fe1c2SSimon Glass    """Return a string summarising the intended action.
26fc3fe1c2SSimon Glass
27fc3fe1c2SSimon Glass    Returns:
28fc3fe1c2SSimon Glass        Summary string.
29fc3fe1c2SSimon Glass    """
30fea5858eSSimon Glass    if commits:
31fea5858eSSimon Glass        count = len(commits)
32fc3fe1c2SSimon Glass        count = (count + options.step - 1) / options.step
33fea5858eSSimon Glass        commit_str = '%d commit%s' % (count, GetPlural(count))
34fea5858eSSimon Glass    else:
35fea5858eSSimon Glass        commit_str = 'current source'
36fea5858eSSimon Glass    str = '%s %s for %d boards' % (
37fea5858eSSimon Glass        'Summary of' if is_summary else 'Building', commit_str,
38fc3fe1c2SSimon Glass        len(selected))
39fc3fe1c2SSimon Glass    str += ' (%d thread%s, %d job%s per thread)' % (options.threads,
40fc3fe1c2SSimon Glass            GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
41fc3fe1c2SSimon Glass    return str
42fc3fe1c2SSimon Glass
43fc3fe1c2SSimon Glassdef ShowActions(series, why_selected, boards_selected, builder, options):
44fc3fe1c2SSimon Glass    """Display a list of actions that we would take, if not a dry run.
45fc3fe1c2SSimon Glass
46fc3fe1c2SSimon Glass    Args:
47fc3fe1c2SSimon Glass        series: Series object
48fc3fe1c2SSimon Glass        why_selected: Dictionary where each key is a buildman argument
49fc3fe1c2SSimon Glass                provided by the user, and the value is the boards brought
50fc3fe1c2SSimon Glass                in by that argument. For example, 'arm' might bring in
51fc3fe1c2SSimon Glass                400 boards, so in this case the key would be 'arm' and
52fc3fe1c2SSimon Glass                the value would be a list of board names.
53fc3fe1c2SSimon Glass        boards_selected: Dict of selected boards, key is target name,
54fc3fe1c2SSimon Glass                value is Board object
55fc3fe1c2SSimon Glass        builder: The builder that will be used to build the commits
56fc3fe1c2SSimon Glass        options: Command line options object
57fc3fe1c2SSimon Glass    """
58fc3fe1c2SSimon Glass    col = terminal.Color()
59fc3fe1c2SSimon Glass    print 'Dry run, so not doing much. But I would do this:'
60fc3fe1c2SSimon Glass    print
61fea5858eSSimon Glass    if series:
62fea5858eSSimon Glass        commits = series.commits
63fea5858eSSimon Glass    else:
64fea5858eSSimon Glass        commits = None
65fea5858eSSimon Glass    print GetActionSummary(False, commits, boards_selected,
66fc3fe1c2SSimon Glass            options)
67fc3fe1c2SSimon Glass    print 'Build directory: %s' % builder.base_dir
68fea5858eSSimon Glass    if commits:
69fc3fe1c2SSimon Glass        for upto in range(0, len(series.commits), options.step):
70fc3fe1c2SSimon Glass            commit = series.commits[upto]
71fc3fe1c2SSimon Glass            print '   ', col.Color(col.YELLOW, commit.hash, bright=False),
72fc3fe1c2SSimon Glass            print commit.subject
73fc3fe1c2SSimon Glass    print
74fc3fe1c2SSimon Glass    for arg in why_selected:
75fc3fe1c2SSimon Glass        if arg != 'all':
76fc3fe1c2SSimon Glass            print arg, ': %d boards' % why_selected[arg]
77fc3fe1c2SSimon Glass    print ('Total boards to build for each commit: %d\n' %
78fc3fe1c2SSimon Glass            why_selected['all'])
79fc3fe1c2SSimon Glass
80fc3fe1c2SSimon Glassdef DoBuildman(options, args):
81fc3fe1c2SSimon Glass    """The main control code for buildman
82fc3fe1c2SSimon Glass
83fc3fe1c2SSimon Glass    Args:
84fc3fe1c2SSimon Glass        options: Command line options object
85fc3fe1c2SSimon Glass        args: Command line arguments (list of strings)
86fc3fe1c2SSimon Glass    """
87*48ba5856SSimon Glass    if options.full_help:
88*48ba5856SSimon Glass        pager = os.getenv('PAGER')
89*48ba5856SSimon Glass        if not pager:
90*48ba5856SSimon Glass            pager = 'more'
91*48ba5856SSimon Glass        fname = os.path.join(os.path.dirname(sys.argv[0]), 'README')
92*48ba5856SSimon Glass        command.Run(pager, fname)
93*48ba5856SSimon Glass        return 0
94*48ba5856SSimon Glass
95fc3fe1c2SSimon Glass    gitutil.Setup()
96fc3fe1c2SSimon Glass
970f7c9ddaSSimon Glass    bsettings.Setup(options.config_file)
98fc3fe1c2SSimon Glass    options.git_dir = os.path.join(options.git, '.git')
99fc3fe1c2SSimon Glass
100fc3fe1c2SSimon Glass    toolchains = toolchain.Toolchains()
101fc3fe1c2SSimon Glass    toolchains.Scan(options.list_tool_chains)
102fc3fe1c2SSimon Glass    if options.list_tool_chains:
103fc3fe1c2SSimon Glass        toolchains.List()
104fc3fe1c2SSimon Glass        print
1052c3deb97SSimon Glass        return 0
106fc3fe1c2SSimon Glass
107fc3fe1c2SSimon Glass    # Work out how many commits to build. We want to build everything on the
108fc3fe1c2SSimon Glass    # branch. We also build the upstream commit as a control so we can see
109fc3fe1c2SSimon Glass    # problems introduced by the first commit on the branch.
110fc3fe1c2SSimon Glass    col = terminal.Color()
111fc3fe1c2SSimon Glass    count = options.count
112fc3fe1c2SSimon Glass    if count == -1:
113fc3fe1c2SSimon Glass        if not options.branch:
114fea5858eSSimon Glass            count = 1
115fea5858eSSimon Glass        else:
116fea5858eSSimon Glass            count = gitutil.CountCommitsInBranch(options.git_dir,
117fea5858eSSimon Glass                                                 options.branch)
118cce717a9SSimon Glass            if count is None:
119fea5858eSSimon Glass                str = ("Branch '%s' not found or has no upstream" %
120fea5858eSSimon Glass                       options.branch)
12131e2141dSMasahiro Yamada                sys.exit(col.Color(col.RED, str))
122fc3fe1c2SSimon Glass            count += 1   # Build upstream commit also
123fc3fe1c2SSimon Glass
124fc3fe1c2SSimon Glass    if not count:
125fc3fe1c2SSimon Glass        str = ("No commits found to process in branch '%s': "
126fc3fe1c2SSimon Glass               "set branch's upstream or use -c flag" % options.branch)
12731e2141dSMasahiro Yamada        sys.exit(col.Color(col.RED, str))
128fc3fe1c2SSimon Glass
129fc3fe1c2SSimon Glass    # Work out what subset of the boards we are building
13073f30b9bSMasahiro Yamada    board_file = os.path.join(options.git, 'boards.cfg')
13173f30b9bSMasahiro Yamada    status = subprocess.call([os.path.join(options.git,
13273f30b9bSMasahiro Yamada                                           'tools/genboardscfg.py')])
13373f30b9bSMasahiro Yamada    if status != 0:
13431e2141dSMasahiro Yamada        sys.exit("Failed to generate boards.cfg")
13573f30b9bSMasahiro Yamada
136fc3fe1c2SSimon Glass    boards = board.Boards()
137fc3fe1c2SSimon Glass    boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
1383cf4ae6fSSimon Glass
1393cf4ae6fSSimon Glass    exclude = []
1403cf4ae6fSSimon Glass    if options.exclude:
1413cf4ae6fSSimon Glass        for arg in options.exclude:
1423cf4ae6fSSimon Glass            exclude += arg.split(',')
1433cf4ae6fSSimon Glass
1443cf4ae6fSSimon Glass    why_selected = boards.SelectBoards(args, exclude)
145fc3fe1c2SSimon Glass    selected = boards.GetSelected()
146fc3fe1c2SSimon Glass    if not len(selected):
14731e2141dSMasahiro Yamada        sys.exit(col.Color(col.RED, 'No matching boards found'))
148fc3fe1c2SSimon Glass
149fc3fe1c2SSimon Glass    # Read the metadata from the commits. First look at the upstream commit,
150fc3fe1c2SSimon Glass    # then the ones in the branch. We would like to do something like
151fc3fe1c2SSimon Glass    # upstream/master~..branch but that isn't possible if upstream/master is
152fc3fe1c2SSimon Glass    # a merge commit (it will list all the commits that form part of the
153fc3fe1c2SSimon Glass    # merge)
154fea5858eSSimon Glass    if options.branch:
1553b74ba5fSSimon Glass        if count == -1:
1563b74ba5fSSimon Glass            range_expr = gitutil.GetRangeInBranch(options.git_dir,
1573b74ba5fSSimon Glass                                                  options.branch)
1583b74ba5fSSimon Glass            upstream_commit = gitutil.GetUpstream(options.git_dir,
1593b74ba5fSSimon Glass                                                  options.branch)
160fea5858eSSimon Glass            series = patchstream.GetMetaDataForList(upstream_commit,
161fea5858eSSimon Glass                options.git_dir, 1)
162fea5858eSSimon Glass
1633b74ba5fSSimon Glass            # Conflicting tags are not a problem for buildman, since it does
1643b74ba5fSSimon Glass            # not use them. For example, Series-version is not useful for
1653b74ba5fSSimon Glass            # buildman. On the other hand conflicting tags will cause an
1663b74ba5fSSimon Glass            # error. So allow later tags to overwrite earlier ones.
167f0b739f1SSimon Glass            series.allow_overwrite = True
1683b74ba5fSSimon Glass            series = patchstream.GetMetaDataForList(range_expr,
1693b74ba5fSSimon Glass                                              options.git_dir, None, series)
1703b74ba5fSSimon Glass        else:
1713b74ba5fSSimon Glass            # Honour the count
1723b74ba5fSSimon Glass            series = patchstream.GetMetaDataForList(options.branch,
1733b74ba5fSSimon Glass                                                    options.git_dir, count)
174fea5858eSSimon Glass    else:
175fea5858eSSimon Glass        series = None
176e5a0e5d8SSimon Glass        options.verbose = True
177e5a0e5d8SSimon Glass        options.show_errors = True
178fc3fe1c2SSimon Glass
179fc3fe1c2SSimon Glass    # By default we have one thread per CPU. But if there are not enough jobs
180fc3fe1c2SSimon Glass    # we can have fewer threads and use a high '-j' value for make.
181fc3fe1c2SSimon Glass    if not options.threads:
182fc3fe1c2SSimon Glass        options.threads = min(multiprocessing.cpu_count(), len(selected))
183fc3fe1c2SSimon Glass    if not options.jobs:
184fc3fe1c2SSimon Glass        options.jobs = max(1, (multiprocessing.cpu_count() +
185fc3fe1c2SSimon Glass                len(selected) - 1) / len(selected))
186fc3fe1c2SSimon Glass
187fc3fe1c2SSimon Glass    if not options.step:
188fc3fe1c2SSimon Glass        options.step = len(series.commits) - 1
189fc3fe1c2SSimon Glass
19099796923SMasahiro Yamada    gnu_make = command.Output(os.path.join(options.git,
19199796923SMasahiro Yamada                                           'scripts/show-gnu-make')).rstrip()
19299796923SMasahiro Yamada    if not gnu_make:
19331e2141dSMasahiro Yamada        sys.exit('GNU Make not found')
19499796923SMasahiro Yamada
195fc3fe1c2SSimon Glass    # Create a new builder with the selected options
196fea5858eSSimon Glass    if options.branch:
197fea5858eSSimon Glass        dirname = options.branch
198fea5858eSSimon Glass    else:
199fea5858eSSimon Glass        dirname = 'current'
200fea5858eSSimon Glass    output_dir = os.path.join(options.output_dir, dirname)
201fc3fe1c2SSimon Glass    builder = Builder(toolchains, output_dir, options.git_dir,
20299796923SMasahiro Yamada            options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
203fc3fe1c2SSimon Glass            show_unknown=options.show_unknown, step=options.step)
204fc3fe1c2SSimon Glass    builder.force_config_on_failure = not options.quick
205fc3fe1c2SSimon Glass
206fc3fe1c2SSimon Glass    # For a dry run, just show our actions as a sanity check
207fc3fe1c2SSimon Glass    if options.dry_run:
208fc3fe1c2SSimon Glass        ShowActions(series, why_selected, selected, builder, options)
209fc3fe1c2SSimon Glass    else:
210fc3fe1c2SSimon Glass        builder.force_build = options.force_build
2114266dc28SSimon Glass        builder.force_build_failures = options.force_build_failures
21297e91526SSimon Glass        builder.force_reconfig = options.force_reconfig
213189a4968SSimon Glass        builder.in_tree = options.in_tree
214fc3fe1c2SSimon Glass
215fc3fe1c2SSimon Glass        # Work out which boards to build
216fc3fe1c2SSimon Glass        board_selected = boards.GetSelectedDict()
217fc3fe1c2SSimon Glass
218fea5858eSSimon Glass        if series:
219fea5858eSSimon Glass            commits = series.commits
220fea5858eSSimon Glass        else:
221fea5858eSSimon Glass            commits = None
222fea5858eSSimon Glass
223fea5858eSSimon Glass        print GetActionSummary(options.summary, commits, board_selected,
224fea5858eSSimon Glass                               options)
225fc3fe1c2SSimon Glass
226b2ea7ab2SSimon Glass        builder.SetDisplayOptions(options.show_errors, options.show_sizes,
227ed966657SSimon Glass                                  options.show_detail, options.show_bloat,
228ed966657SSimon Glass                                  options.list_error_boards)
229fc3fe1c2SSimon Glass        if options.summary:
230fc3fe1c2SSimon Glass            # We can't show function sizes without board details at present
231fc3fe1c2SSimon Glass            if options.show_bloat:
232fc3fe1c2SSimon Glass                options.show_detail = True
233b2ea7ab2SSimon Glass            builder.ShowSummary(commits, board_selected)
234fc3fe1c2SSimon Glass        else:
2352c3deb97SSimon Glass            fail, warned = builder.BuildBoards(commits, board_selected,
236e5a0e5d8SSimon Glass                                options.keep_outputs, options.verbose)
2372c3deb97SSimon Glass            if fail:
2382c3deb97SSimon Glass                return 128
2392c3deb97SSimon Glass            elif warned:
2402c3deb97SSimon Glass                return 129
2412c3deb97SSimon Glass    return 0
242