xref: /rk3399_rockchip-uboot/tools/buildman/control.py (revision 73f30b9b8097b1a25f48fbe035f1dee3dac89317)
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
18*73f30b9bSMasahiro 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
24fc3fe1c2SSimon Glassdef GetActionSummary(is_summary, count, selected, options):
25fc3fe1c2SSimon Glass    """Return a string summarising the intended action.
26fc3fe1c2SSimon Glass
27fc3fe1c2SSimon Glass    Returns:
28fc3fe1c2SSimon Glass        Summary string.
29fc3fe1c2SSimon Glass    """
30fc3fe1c2SSimon Glass    count = (count + options.step - 1) / options.step
31fc3fe1c2SSimon Glass    str = '%s %d commit%s for %d boards' % (
32fc3fe1c2SSimon Glass        'Summary of' if is_summary else 'Building', count, GetPlural(count),
33fc3fe1c2SSimon Glass        len(selected))
34fc3fe1c2SSimon Glass    str += ' (%d thread%s, %d job%s per thread)' % (options.threads,
35fc3fe1c2SSimon Glass            GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
36fc3fe1c2SSimon Glass    return str
37fc3fe1c2SSimon Glass
38fc3fe1c2SSimon Glassdef ShowActions(series, why_selected, boards_selected, builder, options):
39fc3fe1c2SSimon Glass    """Display a list of actions that we would take, if not a dry run.
40fc3fe1c2SSimon Glass
41fc3fe1c2SSimon Glass    Args:
42fc3fe1c2SSimon Glass        series: Series object
43fc3fe1c2SSimon Glass        why_selected: Dictionary where each key is a buildman argument
44fc3fe1c2SSimon Glass                provided by the user, and the value is the boards brought
45fc3fe1c2SSimon Glass                in by that argument. For example, 'arm' might bring in
46fc3fe1c2SSimon Glass                400 boards, so in this case the key would be 'arm' and
47fc3fe1c2SSimon Glass                the value would be a list of board names.
48fc3fe1c2SSimon Glass        boards_selected: Dict of selected boards, key is target name,
49fc3fe1c2SSimon Glass                value is Board object
50fc3fe1c2SSimon Glass        builder: The builder that will be used to build the commits
51fc3fe1c2SSimon Glass        options: Command line options object
52fc3fe1c2SSimon Glass    """
53fc3fe1c2SSimon Glass    col = terminal.Color()
54fc3fe1c2SSimon Glass    print 'Dry run, so not doing much. But I would do this:'
55fc3fe1c2SSimon Glass    print
56fc3fe1c2SSimon Glass    print GetActionSummary(False, len(series.commits), boards_selected,
57fc3fe1c2SSimon Glass            options)
58fc3fe1c2SSimon Glass    print 'Build directory: %s' % builder.base_dir
59fc3fe1c2SSimon Glass    for upto in range(0, len(series.commits), options.step):
60fc3fe1c2SSimon Glass        commit = series.commits[upto]
61fc3fe1c2SSimon Glass        print '   ', col.Color(col.YELLOW, commit.hash, bright=False),
62fc3fe1c2SSimon Glass        print commit.subject
63fc3fe1c2SSimon Glass    print
64fc3fe1c2SSimon Glass    for arg in why_selected:
65fc3fe1c2SSimon Glass        if arg != 'all':
66fc3fe1c2SSimon Glass            print arg, ': %d boards' % why_selected[arg]
67fc3fe1c2SSimon Glass    print ('Total boards to build for each commit: %d\n' %
68fc3fe1c2SSimon Glass            why_selected['all'])
69fc3fe1c2SSimon Glass
70fc3fe1c2SSimon Glassdef DoBuildman(options, args):
71fc3fe1c2SSimon Glass    """The main control code for buildman
72fc3fe1c2SSimon Glass
73fc3fe1c2SSimon Glass    Args:
74fc3fe1c2SSimon Glass        options: Command line options object
75fc3fe1c2SSimon Glass        args: Command line arguments (list of strings)
76fc3fe1c2SSimon Glass    """
77fc3fe1c2SSimon Glass    gitutil.Setup()
78fc3fe1c2SSimon Glass
79fc3fe1c2SSimon Glass    bsettings.Setup()
80fc3fe1c2SSimon Glass    options.git_dir = os.path.join(options.git, '.git')
81fc3fe1c2SSimon Glass
82fc3fe1c2SSimon Glass    toolchains = toolchain.Toolchains()
83fc3fe1c2SSimon Glass    toolchains.Scan(options.list_tool_chains)
84fc3fe1c2SSimon Glass    if options.list_tool_chains:
85fc3fe1c2SSimon Glass        toolchains.List()
86fc3fe1c2SSimon Glass        print
87fc3fe1c2SSimon Glass        return
88fc3fe1c2SSimon Glass
89fc3fe1c2SSimon Glass    # Work out how many commits to build. We want to build everything on the
90fc3fe1c2SSimon Glass    # branch. We also build the upstream commit as a control so we can see
91fc3fe1c2SSimon Glass    # problems introduced by the first commit on the branch.
92fc3fe1c2SSimon Glass    col = terminal.Color()
93fc3fe1c2SSimon Glass    count = options.count
94fc3fe1c2SSimon Glass    if count == -1:
95fc3fe1c2SSimon Glass        if not options.branch:
96fc3fe1c2SSimon Glass            str = 'Please use -b to specify a branch to build'
97fc3fe1c2SSimon Glass            print col.Color(col.RED, str)
98fc3fe1c2SSimon Glass            sys.exit(1)
99fc3fe1c2SSimon Glass        count = gitutil.CountCommitsInBranch(options.git_dir, options.branch)
100cce717a9SSimon Glass        if count is None:
101cce717a9SSimon Glass            str = "Branch '%s' not found or has no upstream" % options.branch
102cce717a9SSimon Glass            print col.Color(col.RED, str)
103cce717a9SSimon Glass            sys.exit(1)
104fc3fe1c2SSimon Glass        count += 1   # Build upstream commit also
105fc3fe1c2SSimon Glass
106fc3fe1c2SSimon Glass    if not count:
107fc3fe1c2SSimon Glass        str = ("No commits found to process in branch '%s': "
108fc3fe1c2SSimon Glass               "set branch's upstream or use -c flag" % options.branch)
109fc3fe1c2SSimon Glass        print col.Color(col.RED, str)
110fc3fe1c2SSimon Glass        sys.exit(1)
111fc3fe1c2SSimon Glass
112fc3fe1c2SSimon Glass    # Work out what subset of the boards we are building
113*73f30b9bSMasahiro Yamada    board_file = os.path.join(options.git, 'boards.cfg')
114*73f30b9bSMasahiro Yamada    if not os.path.exists(board_file):
115*73f30b9bSMasahiro Yamada        print 'Could not find %s' % board_file
116*73f30b9bSMasahiro Yamada        status = subprocess.call([os.path.join(options.git,
117*73f30b9bSMasahiro Yamada                                               'tools/genboardscfg.py')])
118*73f30b9bSMasahiro Yamada        if status != 0:
119*73f30b9bSMasahiro Yamada            print >> sys.stderr, "Failed to generate boards.cfg"
120*73f30b9bSMasahiro Yamada            sys.exit(1)
121*73f30b9bSMasahiro Yamada
122fc3fe1c2SSimon Glass    boards = board.Boards()
123fc3fe1c2SSimon Glass    boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
124fc3fe1c2SSimon Glass    why_selected = boards.SelectBoards(args)
125fc3fe1c2SSimon Glass    selected = boards.GetSelected()
126fc3fe1c2SSimon Glass    if not len(selected):
127fc3fe1c2SSimon Glass        print col.Color(col.RED, 'No matching boards found')
128fc3fe1c2SSimon Glass        sys.exit(1)
129fc3fe1c2SSimon Glass
130fc3fe1c2SSimon Glass    # Read the metadata from the commits. First look at the upstream commit,
131fc3fe1c2SSimon Glass    # then the ones in the branch. We would like to do something like
132fc3fe1c2SSimon Glass    # upstream/master~..branch but that isn't possible if upstream/master is
133fc3fe1c2SSimon Glass    # a merge commit (it will list all the commits that form part of the
134fc3fe1c2SSimon Glass    # merge)
135fc3fe1c2SSimon Glass    range_expr = gitutil.GetRangeInBranch(options.git_dir, options.branch)
136fc3fe1c2SSimon Glass    upstream_commit = gitutil.GetUpstream(options.git_dir, options.branch)
137fc3fe1c2SSimon Glass    series = patchstream.GetMetaDataForList(upstream_commit, options.git_dir,
138fc3fe1c2SSimon Glass            1)
139f0b739f1SSimon Glass    # Conflicting tags are not a problem for buildman, since it does not use
140f0b739f1SSimon Glass    # them. For example, Series-version is not useful for buildman. On the
141f0b739f1SSimon Glass    # other hand conflicting tags will cause an error. So allow later tags
142f0b739f1SSimon Glass    # to overwrite earlier ones.
143f0b739f1SSimon Glass    series.allow_overwrite = True
144fc3fe1c2SSimon Glass    series = patchstream.GetMetaDataForList(range_expr, options.git_dir, None,
145fc3fe1c2SSimon Glass            series)
146fc3fe1c2SSimon Glass
147fc3fe1c2SSimon Glass    # By default we have one thread per CPU. But if there are not enough jobs
148fc3fe1c2SSimon Glass    # we can have fewer threads and use a high '-j' value for make.
149fc3fe1c2SSimon Glass    if not options.threads:
150fc3fe1c2SSimon Glass        options.threads = min(multiprocessing.cpu_count(), len(selected))
151fc3fe1c2SSimon Glass    if not options.jobs:
152fc3fe1c2SSimon Glass        options.jobs = max(1, (multiprocessing.cpu_count() +
153fc3fe1c2SSimon Glass                len(selected) - 1) / len(selected))
154fc3fe1c2SSimon Glass
155fc3fe1c2SSimon Glass    if not options.step:
156fc3fe1c2SSimon Glass        options.step = len(series.commits) - 1
157fc3fe1c2SSimon Glass
15899796923SMasahiro Yamada    gnu_make = command.Output(os.path.join(options.git,
15999796923SMasahiro Yamada                                           'scripts/show-gnu-make')).rstrip()
16099796923SMasahiro Yamada    if not gnu_make:
16199796923SMasahiro Yamada        print >> sys.stderr, 'GNU Make not found'
16299796923SMasahiro Yamada        sys.exit(1)
16399796923SMasahiro Yamada
164fc3fe1c2SSimon Glass    # Create a new builder with the selected options
165e0ba9299SDaniel Schwierzeck    output_dir = os.path.join(options.output_dir, options.branch)
166fc3fe1c2SSimon Glass    builder = Builder(toolchains, output_dir, options.git_dir,
16799796923SMasahiro Yamada            options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
168fc3fe1c2SSimon Glass            show_unknown=options.show_unknown, step=options.step)
169fc3fe1c2SSimon Glass    builder.force_config_on_failure = not options.quick
170fc3fe1c2SSimon Glass
171fc3fe1c2SSimon Glass    # For a dry run, just show our actions as a sanity check
172fc3fe1c2SSimon Glass    if options.dry_run:
173fc3fe1c2SSimon Glass        ShowActions(series, why_selected, selected, builder, options)
174fc3fe1c2SSimon Glass    else:
175fc3fe1c2SSimon Glass        builder.force_build = options.force_build
1764266dc28SSimon Glass        builder.force_build_failures = options.force_build_failures
17797e91526SSimon Glass        builder.force_reconfig = options.force_reconfig
178189a4968SSimon Glass        builder.in_tree = options.in_tree
179fc3fe1c2SSimon Glass
180fc3fe1c2SSimon Glass        # Work out which boards to build
181fc3fe1c2SSimon Glass        board_selected = boards.GetSelectedDict()
182fc3fe1c2SSimon Glass
183fc3fe1c2SSimon Glass        print GetActionSummary(options.summary, count, board_selected, options)
184fc3fe1c2SSimon Glass
185fc3fe1c2SSimon Glass        if options.summary:
186fc3fe1c2SSimon Glass            # We can't show function sizes without board details at present
187fc3fe1c2SSimon Glass            if options.show_bloat:
188fc3fe1c2SSimon Glass                options.show_detail = True
189fc3fe1c2SSimon Glass            builder.ShowSummary(series.commits, board_selected,
190fc3fe1c2SSimon Glass                    options.show_errors, options.show_sizes,
191fc3fe1c2SSimon Glass                    options.show_detail, options.show_bloat)
192fc3fe1c2SSimon Glass        else:
193fc3fe1c2SSimon Glass            builder.BuildBoards(series.commits, board_selected,
194fc3fe1c2SSimon Glass                    options.show_errors, options.keep_outputs)
195