xref: /rk3399_rockchip-uboot/tools/buildman/control.py (revision cce717a96c5840b0cf33e30c87fb1f2b8d633ee3)
1fc3fe1c2SSimon Glass# Copyright (c) 2013 The Chromium OS Authors.
2fc3fe1c2SSimon Glass#
3fc3fe1c2SSimon Glass# See file CREDITS for list of people who contributed to this
4fc3fe1c2SSimon Glass# project.
5fc3fe1c2SSimon Glass#
6fc3fe1c2SSimon Glass# This program is free software; you can redistribute it and/or
7fc3fe1c2SSimon Glass# modify it under the terms of the GNU General Public License as
8fc3fe1c2SSimon Glass# published by the Free Software Foundation; either version 2 of
9fc3fe1c2SSimon Glass# the License, or (at your option) any later version.
10fc3fe1c2SSimon Glass#
11fc3fe1c2SSimon Glass# This program is distributed in the hope that it will be useful,
12fc3fe1c2SSimon Glass# but WITHOUT ANY WARRANTY; without even the implied warranty of
13fc3fe1c2SSimon Glass# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14fc3fe1c2SSimon Glass# GNU General Public License for more details.
15fc3fe1c2SSimon Glass#
16fc3fe1c2SSimon Glass# You should have received a copy of the GNU General Public License
17fc3fe1c2SSimon Glass# along with this program; if not, write to the Free Software
18fc3fe1c2SSimon Glass# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19fc3fe1c2SSimon Glass# MA 02111-1307 USA
20fc3fe1c2SSimon Glass#
21fc3fe1c2SSimon Glass
22fc3fe1c2SSimon Glassimport multiprocessing
23fc3fe1c2SSimon Glassimport os
24fc3fe1c2SSimon Glassimport sys
25fc3fe1c2SSimon Glass
26fc3fe1c2SSimon Glassimport board
27fc3fe1c2SSimon Glassimport bsettings
28fc3fe1c2SSimon Glassfrom builder import Builder
29fc3fe1c2SSimon Glassimport gitutil
30fc3fe1c2SSimon Glassimport patchstream
31fc3fe1c2SSimon Glassimport terminal
32fc3fe1c2SSimon Glassimport toolchain
33fc3fe1c2SSimon Glass
34fc3fe1c2SSimon Glassdef GetPlural(count):
35fc3fe1c2SSimon Glass    """Returns a plural 's' if count is not 1"""
36fc3fe1c2SSimon Glass    return 's' if count != 1 else ''
37fc3fe1c2SSimon Glass
38fc3fe1c2SSimon Glassdef GetActionSummary(is_summary, count, selected, options):
39fc3fe1c2SSimon Glass    """Return a string summarising the intended action.
40fc3fe1c2SSimon Glass
41fc3fe1c2SSimon Glass    Returns:
42fc3fe1c2SSimon Glass        Summary string.
43fc3fe1c2SSimon Glass    """
44fc3fe1c2SSimon Glass    count = (count + options.step - 1) / options.step
45fc3fe1c2SSimon Glass    str = '%s %d commit%s for %d boards' % (
46fc3fe1c2SSimon Glass        'Summary of' if is_summary else 'Building', count, GetPlural(count),
47fc3fe1c2SSimon Glass        len(selected))
48fc3fe1c2SSimon Glass    str += ' (%d thread%s, %d job%s per thread)' % (options.threads,
49fc3fe1c2SSimon Glass            GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
50fc3fe1c2SSimon Glass    return str
51fc3fe1c2SSimon Glass
52fc3fe1c2SSimon Glassdef ShowActions(series, why_selected, boards_selected, builder, options):
53fc3fe1c2SSimon Glass    """Display a list of actions that we would take, if not a dry run.
54fc3fe1c2SSimon Glass
55fc3fe1c2SSimon Glass    Args:
56fc3fe1c2SSimon Glass        series: Series object
57fc3fe1c2SSimon Glass        why_selected: Dictionary where each key is a buildman argument
58fc3fe1c2SSimon Glass                provided by the user, and the value is the boards brought
59fc3fe1c2SSimon Glass                in by that argument. For example, 'arm' might bring in
60fc3fe1c2SSimon Glass                400 boards, so in this case the key would be 'arm' and
61fc3fe1c2SSimon Glass                the value would be a list of board names.
62fc3fe1c2SSimon Glass        boards_selected: Dict of selected boards, key is target name,
63fc3fe1c2SSimon Glass                value is Board object
64fc3fe1c2SSimon Glass        builder: The builder that will be used to build the commits
65fc3fe1c2SSimon Glass        options: Command line options object
66fc3fe1c2SSimon Glass    """
67fc3fe1c2SSimon Glass    col = terminal.Color()
68fc3fe1c2SSimon Glass    print 'Dry run, so not doing much. But I would do this:'
69fc3fe1c2SSimon Glass    print
70fc3fe1c2SSimon Glass    print GetActionSummary(False, len(series.commits), boards_selected,
71fc3fe1c2SSimon Glass            options)
72fc3fe1c2SSimon Glass    print 'Build directory: %s' % builder.base_dir
73fc3fe1c2SSimon Glass    for upto in range(0, len(series.commits), options.step):
74fc3fe1c2SSimon Glass        commit = series.commits[upto]
75fc3fe1c2SSimon Glass        print '   ', col.Color(col.YELLOW, commit.hash, bright=False),
76fc3fe1c2SSimon Glass        print commit.subject
77fc3fe1c2SSimon Glass    print
78fc3fe1c2SSimon Glass    for arg in why_selected:
79fc3fe1c2SSimon Glass        if arg != 'all':
80fc3fe1c2SSimon Glass            print arg, ': %d boards' % why_selected[arg]
81fc3fe1c2SSimon Glass    print ('Total boards to build for each commit: %d\n' %
82fc3fe1c2SSimon Glass            why_selected['all'])
83fc3fe1c2SSimon Glass
84fc3fe1c2SSimon Glassdef DoBuildman(options, args):
85fc3fe1c2SSimon Glass    """The main control code for buildman
86fc3fe1c2SSimon Glass
87fc3fe1c2SSimon Glass    Args:
88fc3fe1c2SSimon Glass        options: Command line options object
89fc3fe1c2SSimon Glass        args: Command line arguments (list of strings)
90fc3fe1c2SSimon Glass    """
91fc3fe1c2SSimon Glass    gitutil.Setup()
92fc3fe1c2SSimon Glass
93fc3fe1c2SSimon Glass    bsettings.Setup()
94fc3fe1c2SSimon Glass    options.git_dir = os.path.join(options.git, '.git')
95fc3fe1c2SSimon Glass
96fc3fe1c2SSimon Glass    toolchains = toolchain.Toolchains()
97fc3fe1c2SSimon Glass    toolchains.Scan(options.list_tool_chains)
98fc3fe1c2SSimon Glass    if options.list_tool_chains:
99fc3fe1c2SSimon Glass        toolchains.List()
100fc3fe1c2SSimon Glass        print
101fc3fe1c2SSimon Glass        return
102fc3fe1c2SSimon Glass
103fc3fe1c2SSimon Glass    # Work out how many commits to build. We want to build everything on the
104fc3fe1c2SSimon Glass    # branch. We also build the upstream commit as a control so we can see
105fc3fe1c2SSimon Glass    # problems introduced by the first commit on the branch.
106fc3fe1c2SSimon Glass    col = terminal.Color()
107fc3fe1c2SSimon Glass    count = options.count
108fc3fe1c2SSimon Glass    if count == -1:
109fc3fe1c2SSimon Glass        if not options.branch:
110fc3fe1c2SSimon Glass            str = 'Please use -b to specify a branch to build'
111fc3fe1c2SSimon Glass            print col.Color(col.RED, str)
112fc3fe1c2SSimon Glass            sys.exit(1)
113fc3fe1c2SSimon Glass        count = gitutil.CountCommitsInBranch(options.git_dir, options.branch)
114*cce717a9SSimon Glass        if count is None:
115*cce717a9SSimon Glass            str = "Branch '%s' not found or has no upstream" % options.branch
116*cce717a9SSimon Glass            print col.Color(col.RED, str)
117*cce717a9SSimon Glass            sys.exit(1)
118fc3fe1c2SSimon Glass        count += 1   # Build upstream commit also
119fc3fe1c2SSimon Glass
120fc3fe1c2SSimon Glass    if not count:
121fc3fe1c2SSimon Glass        str = ("No commits found to process in branch '%s': "
122fc3fe1c2SSimon Glass               "set branch's upstream or use -c flag" % options.branch)
123fc3fe1c2SSimon Glass        print col.Color(col.RED, str)
124fc3fe1c2SSimon Glass        sys.exit(1)
125fc3fe1c2SSimon Glass
126fc3fe1c2SSimon Glass    # Work out what subset of the boards we are building
127fc3fe1c2SSimon Glass    boards = board.Boards()
128fc3fe1c2SSimon Glass    boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
129fc3fe1c2SSimon Glass    why_selected = boards.SelectBoards(args)
130fc3fe1c2SSimon Glass    selected = boards.GetSelected()
131fc3fe1c2SSimon Glass    if not len(selected):
132fc3fe1c2SSimon Glass        print col.Color(col.RED, 'No matching boards found')
133fc3fe1c2SSimon Glass        sys.exit(1)
134fc3fe1c2SSimon Glass
135fc3fe1c2SSimon Glass    # Read the metadata from the commits. First look at the upstream commit,
136fc3fe1c2SSimon Glass    # then the ones in the branch. We would like to do something like
137fc3fe1c2SSimon Glass    # upstream/master~..branch but that isn't possible if upstream/master is
138fc3fe1c2SSimon Glass    # a merge commit (it will list all the commits that form part of the
139fc3fe1c2SSimon Glass    # merge)
140fc3fe1c2SSimon Glass    range_expr = gitutil.GetRangeInBranch(options.git_dir, options.branch)
141fc3fe1c2SSimon Glass    upstream_commit = gitutil.GetUpstream(options.git_dir, options.branch)
142fc3fe1c2SSimon Glass    series = patchstream.GetMetaDataForList(upstream_commit, options.git_dir,
143fc3fe1c2SSimon Glass            1)
144f0b739f1SSimon Glass    # Conflicting tags are not a problem for buildman, since it does not use
145f0b739f1SSimon Glass    # them. For example, Series-version is not useful for buildman. On the
146f0b739f1SSimon Glass    # other hand conflicting tags will cause an error. So allow later tags
147f0b739f1SSimon Glass    # to overwrite earlier ones.
148f0b739f1SSimon Glass    series.allow_overwrite = True
149fc3fe1c2SSimon Glass    series = patchstream.GetMetaDataForList(range_expr, options.git_dir, None,
150fc3fe1c2SSimon Glass            series)
151fc3fe1c2SSimon Glass
152fc3fe1c2SSimon Glass    # By default we have one thread per CPU. But if there are not enough jobs
153fc3fe1c2SSimon Glass    # we can have fewer threads and use a high '-j' value for make.
154fc3fe1c2SSimon Glass    if not options.threads:
155fc3fe1c2SSimon Glass        options.threads = min(multiprocessing.cpu_count(), len(selected))
156fc3fe1c2SSimon Glass    if not options.jobs:
157fc3fe1c2SSimon Glass        options.jobs = max(1, (multiprocessing.cpu_count() +
158fc3fe1c2SSimon Glass                len(selected) - 1) / len(selected))
159fc3fe1c2SSimon Glass
160fc3fe1c2SSimon Glass    if not options.step:
161fc3fe1c2SSimon Glass        options.step = len(series.commits) - 1
162fc3fe1c2SSimon Glass
163fc3fe1c2SSimon Glass    # Create a new builder with the selected options
164fc3fe1c2SSimon Glass    output_dir = os.path.join('..', options.branch)
165fc3fe1c2SSimon Glass    builder = Builder(toolchains, output_dir, options.git_dir,
166fc3fe1c2SSimon Glass            options.threads, options.jobs, checkout=True,
167fc3fe1c2SSimon Glass            show_unknown=options.show_unknown, step=options.step)
168fc3fe1c2SSimon Glass    builder.force_config_on_failure = not options.quick
169fc3fe1c2SSimon Glass
170fc3fe1c2SSimon Glass    # For a dry run, just show our actions as a sanity check
171fc3fe1c2SSimon Glass    if options.dry_run:
172fc3fe1c2SSimon Glass        ShowActions(series, why_selected, selected, builder, options)
173fc3fe1c2SSimon Glass    else:
174fc3fe1c2SSimon Glass        builder.force_build = options.force_build
175fc3fe1c2SSimon Glass
176fc3fe1c2SSimon Glass        # Work out which boards to build
177fc3fe1c2SSimon Glass        board_selected = boards.GetSelectedDict()
178fc3fe1c2SSimon Glass
179fc3fe1c2SSimon Glass        print GetActionSummary(options.summary, count, board_selected, options)
180fc3fe1c2SSimon Glass
181fc3fe1c2SSimon Glass        if options.summary:
182fc3fe1c2SSimon Glass            # We can't show function sizes without board details at present
183fc3fe1c2SSimon Glass            if options.show_bloat:
184fc3fe1c2SSimon Glass                options.show_detail = True
185fc3fe1c2SSimon Glass            builder.ShowSummary(series.commits, board_selected,
186fc3fe1c2SSimon Glass                    options.show_errors, options.show_sizes,
187fc3fe1c2SSimon Glass                    options.show_detail, options.show_bloat)
188fc3fe1c2SSimon Glass        else:
189fc3fe1c2SSimon Glass            builder.BuildBoards(series.commits, board_selected,
190fc3fe1c2SSimon Glass                    options.show_errors, options.keep_outputs)
191