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