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