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