mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-31 03:58:17 +00:00 
			
		
		
		
	Fix a typo in the error message from CheckOutputDir(). Signed-off-by: Chris Packham <judge.packham@gmail.com> Reviewed-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			348 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			348 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # SPDX-License-Identifier: GPL-2.0+
 | |
| # Copyright (c) 2013 The Chromium OS Authors.
 | |
| #
 | |
| 
 | |
| import multiprocessing
 | |
| import os
 | |
| import shutil
 | |
| import sys
 | |
| 
 | |
| import board
 | |
| import bsettings
 | |
| from builder import Builder
 | |
| import gitutil
 | |
| import patchstream
 | |
| import terminal
 | |
| from terminal import Print
 | |
| import toolchain
 | |
| import command
 | |
| import subprocess
 | |
| 
 | |
| def GetPlural(count):
 | |
|     """Returns a plural 's' if count is not 1"""
 | |
|     return 's' if count != 1 else ''
 | |
| 
 | |
| def GetActionSummary(is_summary, commits, selected, options):
 | |
|     """Return a string summarising the intended action.
 | |
| 
 | |
|     Returns:
 | |
|         Summary string.
 | |
|     """
 | |
|     if commits:
 | |
|         count = len(commits)
 | |
|         count = (count + options.step - 1) / options.step
 | |
|         commit_str = '%d commit%s' % (count, GetPlural(count))
 | |
|     else:
 | |
|         commit_str = 'current source'
 | |
|     str = '%s %s for %d boards' % (
 | |
|         'Summary of' if is_summary else 'Building', commit_str,
 | |
|         len(selected))
 | |
|     str += ' (%d thread%s, %d job%s per thread)' % (options.threads,
 | |
|             GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
 | |
|     return str
 | |
| 
 | |
| def ShowActions(series, why_selected, boards_selected, builder, options,
 | |
|                 board_warnings):
 | |
|     """Display a list of actions that we would take, if not a dry run.
 | |
| 
 | |
|     Args:
 | |
|         series: Series object
 | |
|         why_selected: Dictionary where each key is a buildman argument
 | |
|                 provided by the user, and the value is the list of boards
 | |
|                 brought in by that argument. For example, 'arm' might bring
 | |
|                 in 400 boards, so in this case the key would be 'arm' and
 | |
|                 the value would be a list of board names.
 | |
|         boards_selected: Dict of selected boards, key is target name,
 | |
|                 value is Board object
 | |
|         builder: The builder that will be used to build the commits
 | |
|         options: Command line options object
 | |
|         board_warnings: List of warnings obtained from board selected
 | |
|     """
 | |
|     col = terminal.Color()
 | |
|     print 'Dry run, so not doing much. But I would do this:'
 | |
|     print
 | |
|     if series:
 | |
|         commits = series.commits
 | |
|     else:
 | |
|         commits = None
 | |
|     print GetActionSummary(False, commits, boards_selected,
 | |
|             options)
 | |
|     print 'Build directory: %s' % builder.base_dir
 | |
|     if commits:
 | |
|         for upto in range(0, len(series.commits), options.step):
 | |
|             commit = series.commits[upto]
 | |
|             print '   ', col.Color(col.YELLOW, commit.hash[:8], bright=False),
 | |
|             print commit.subject
 | |
|     print
 | |
|     for arg in why_selected:
 | |
|         if arg != 'all':
 | |
|             print arg, ': %d boards' % len(why_selected[arg])
 | |
|             if options.verbose:
 | |
|                 print '   %s' % ' '.join(why_selected[arg])
 | |
|     print ('Total boards to build for each commit: %d\n' %
 | |
|             len(why_selected['all']))
 | |
|     if board_warnings:
 | |
|         for warning in board_warnings:
 | |
|             print col.Color(col.YELLOW, warning)
 | |
| 
 | |
| def CheckOutputDir(output_dir):
 | |
|     """Make sure that the output directory is not within the current directory
 | |
| 
 | |
|     If we try to use an output directory which is within the current directory
 | |
|     (which is assumed to hold the U-Boot source) we may end up deleting the
 | |
|     U-Boot source code. Detect this and print an error in this case.
 | |
| 
 | |
|     Args:
 | |
|         output_dir: Output directory path to check
 | |
|     """
 | |
|     path = os.path.realpath(output_dir)
 | |
|     cwd_path = os.path.realpath('.')
 | |
|     while True:
 | |
|         if os.path.realpath(path) == cwd_path:
 | |
|             Print("Cannot use output directory '%s' since it is within the current directory '%s'" %
 | |
|                   (path, cwd_path))
 | |
|             sys.exit(1)
 | |
|         parent = os.path.dirname(path)
 | |
|         if parent == path:
 | |
|             break
 | |
|         path = parent
 | |
| 
 | |
| def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
 | |
|                clean_dir=False):
 | |
|     """The main control code for buildman
 | |
| 
 | |
|     Args:
 | |
|         options: Command line options object
 | |
|         args: Command line arguments (list of strings)
 | |
|         toolchains: Toolchains to use - this should be a Toolchains()
 | |
|                 object. If None, then it will be created and scanned
 | |
|         make_func: Make function to use for the builder. This is called
 | |
|                 to execute 'make'. If this is None, the normal function
 | |
|                 will be used, which calls the 'make' tool with suitable
 | |
|                 arguments. This setting is useful for tests.
 | |
|         board: Boards() object to use, containing a list of available
 | |
|                 boards. If this is None it will be created and scanned.
 | |
|     """
 | |
|     global builder
 | |
| 
 | |
|     if options.full_help:
 | |
|         pager = os.getenv('PAGER')
 | |
|         if not pager:
 | |
|             pager = 'more'
 | |
|         fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
 | |
|                              'README')
 | |
|         command.Run(pager, fname)
 | |
|         return 0
 | |
| 
 | |
|     gitutil.Setup()
 | |
|     col = terminal.Color()
 | |
| 
 | |
|     options.git_dir = os.path.join(options.git, '.git')
 | |
| 
 | |
|     no_toolchains = toolchains is None
 | |
|     if no_toolchains:
 | |
|         toolchains = toolchain.Toolchains(options.override_toolchain)
 | |
| 
 | |
|     if options.fetch_arch:
 | |
|         if options.fetch_arch == 'list':
 | |
|             sorted_list = toolchains.ListArchs()
 | |
|             print col.Color(col.BLUE, 'Available architectures: %s\n' %
 | |
|                             ' '.join(sorted_list))
 | |
|             return 0
 | |
|         else:
 | |
|             fetch_arch = options.fetch_arch
 | |
|             if fetch_arch == 'all':
 | |
|                 fetch_arch = ','.join(toolchains.ListArchs())
 | |
|                 print col.Color(col.CYAN, '\nDownloading toolchains: %s' %
 | |
|                                 fetch_arch)
 | |
|             for arch in fetch_arch.split(','):
 | |
|                 print
 | |
|                 ret = toolchains.FetchAndInstall(arch)
 | |
|                 if ret:
 | |
|                     return ret
 | |
|             return 0
 | |
| 
 | |
|     if no_toolchains:
 | |
|         toolchains.GetSettings()
 | |
|         toolchains.Scan(options.list_tool_chains and options.verbose)
 | |
|     if options.list_tool_chains:
 | |
|         toolchains.List()
 | |
|         print
 | |
|         return 0
 | |
| 
 | |
|     # Work out how many commits to build. We want to build everything on the
 | |
|     # branch. We also build the upstream commit as a control so we can see
 | |
|     # problems introduced by the first commit on the branch.
 | |
|     count = options.count
 | |
|     has_range = options.branch and '..' in options.branch
 | |
|     if count == -1:
 | |
|         if not options.branch:
 | |
|             count = 1
 | |
|         else:
 | |
|             if has_range:
 | |
|                 count, msg = gitutil.CountCommitsInRange(options.git_dir,
 | |
|                                                          options.branch)
 | |
|             else:
 | |
|                 count, msg = gitutil.CountCommitsInBranch(options.git_dir,
 | |
|                                                           options.branch)
 | |
|             if count is None:
 | |
|                 sys.exit(col.Color(col.RED, msg))
 | |
|             elif count == 0:
 | |
|                 sys.exit(col.Color(col.RED, "Range '%s' has no commits" %
 | |
|                                    options.branch))
 | |
|             if msg:
 | |
|                 print col.Color(col.YELLOW, msg)
 | |
|             count += 1   # Build upstream commit also
 | |
| 
 | |
|     if not count:
 | |
|         str = ("No commits found to process in branch '%s': "
 | |
|                "set branch's upstream or use -c flag" % options.branch)
 | |
|         sys.exit(col.Color(col.RED, str))
 | |
| 
 | |
|     # Work out what subset of the boards we are building
 | |
|     if not boards:
 | |
|         board_file = os.path.join(options.git, 'boards.cfg')
 | |
|         status = subprocess.call([os.path.join(options.git,
 | |
|                                                 'tools/genboardscfg.py')])
 | |
|         if status != 0:
 | |
|                 sys.exit("Failed to generate boards.cfg")
 | |
| 
 | |
|         boards = board.Boards()
 | |
|         boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
 | |
| 
 | |
|     exclude = []
 | |
|     if options.exclude:
 | |
|         for arg in options.exclude:
 | |
|             exclude += arg.split(',')
 | |
| 
 | |
| 
 | |
|     if options.boards:
 | |
|         requested_boards = []
 | |
|         for b in options.boards:
 | |
|             requested_boards += b.split(',')
 | |
|     else:
 | |
|         requested_boards = None
 | |
|     why_selected, board_warnings = boards.SelectBoards(args, exclude,
 | |
|                                                        requested_boards)
 | |
|     selected = boards.GetSelected()
 | |
|     if not len(selected):
 | |
|         sys.exit(col.Color(col.RED, 'No matching boards found'))
 | |
| 
 | |
|     # Read the metadata from the commits. First look at the upstream commit,
 | |
|     # then the ones in the branch. We would like to do something like
 | |
|     # upstream/master~..branch but that isn't possible if upstream/master is
 | |
|     # a merge commit (it will list all the commits that form part of the
 | |
|     # merge)
 | |
|     # Conflicting tags are not a problem for buildman, since it does not use
 | |
|     # them. For example, Series-version is not useful for buildman. On the
 | |
|     # other hand conflicting tags will cause an error. So allow later tags
 | |
|     # to overwrite earlier ones by setting allow_overwrite=True
 | |
|     if options.branch:
 | |
|         if count == -1:
 | |
|             if has_range:
 | |
|                 range_expr = options.branch
 | |
|             else:
 | |
|                 range_expr = gitutil.GetRangeInBranch(options.git_dir,
 | |
|                                                       options.branch)
 | |
|             upstream_commit = gitutil.GetUpstream(options.git_dir,
 | |
|                                                   options.branch)
 | |
|             series = patchstream.GetMetaDataForList(upstream_commit,
 | |
|                 options.git_dir, 1, series=None, allow_overwrite=True)
 | |
| 
 | |
|             series = patchstream.GetMetaDataForList(range_expr,
 | |
|                     options.git_dir, None, series, allow_overwrite=True)
 | |
|         else:
 | |
|             # Honour the count
 | |
|             series = patchstream.GetMetaDataForList(options.branch,
 | |
|                     options.git_dir, count, series=None, allow_overwrite=True)
 | |
|     else:
 | |
|         series = None
 | |
|         if not options.dry_run:
 | |
|             options.verbose = True
 | |
|             if not options.summary:
 | |
|                 options.show_errors = True
 | |
| 
 | |
|     # By default we have one thread per CPU. But if there are not enough jobs
 | |
|     # we can have fewer threads and use a high '-j' value for make.
 | |
|     if not options.threads:
 | |
|         options.threads = min(multiprocessing.cpu_count(), len(selected))
 | |
|     if not options.jobs:
 | |
|         options.jobs = max(1, (multiprocessing.cpu_count() +
 | |
|                 len(selected) - 1) / len(selected))
 | |
| 
 | |
|     if not options.step:
 | |
|         options.step = len(series.commits) - 1
 | |
| 
 | |
|     gnu_make = command.Output(os.path.join(options.git,
 | |
|             'scripts/show-gnu-make'), raise_on_error=False).rstrip()
 | |
|     if not gnu_make:
 | |
|         sys.exit('GNU Make not found')
 | |
| 
 | |
|     # Create a new builder with the selected options.
 | |
|     output_dir = options.output_dir
 | |
|     if options.branch:
 | |
|         dirname = options.branch.replace('/', '_')
 | |
|         # As a special case allow the board directory to be placed in the
 | |
|         # output directory itself rather than any subdirectory.
 | |
|         if not options.no_subdirs:
 | |
|             output_dir = os.path.join(options.output_dir, dirname)
 | |
|         if clean_dir and os.path.exists(output_dir):
 | |
|             shutil.rmtree(output_dir)
 | |
|     CheckOutputDir(output_dir)
 | |
|     builder = Builder(toolchains, output_dir, options.git_dir,
 | |
|             options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
 | |
|             show_unknown=options.show_unknown, step=options.step,
 | |
|             no_subdirs=options.no_subdirs, full_path=options.full_path,
 | |
|             verbose_build=options.verbose_build,
 | |
|             incremental=options.incremental,
 | |
|             per_board_out_dir=options.per_board_out_dir,
 | |
|             config_only=options.config_only,
 | |
|             squash_config_y=not options.preserve_config_y,
 | |
|             warnings_as_errors=options.warnings_as_errors)
 | |
|     builder.force_config_on_failure = not options.quick
 | |
|     if make_func:
 | |
|         builder.do_make = make_func
 | |
| 
 | |
|     # For a dry run, just show our actions as a sanity check
 | |
|     if options.dry_run:
 | |
|         ShowActions(series, why_selected, selected, builder, options,
 | |
|                     board_warnings)
 | |
|     else:
 | |
|         builder.force_build = options.force_build
 | |
|         builder.force_build_failures = options.force_build_failures
 | |
|         builder.force_reconfig = options.force_reconfig
 | |
|         builder.in_tree = options.in_tree
 | |
| 
 | |
|         # Work out which boards to build
 | |
|         board_selected = boards.GetSelectedDict()
 | |
| 
 | |
|         if series:
 | |
|             commits = series.commits
 | |
|             # Number the commits for test purposes
 | |
|             for commit in range(len(commits)):
 | |
|                 commits[commit].sequence = commit
 | |
|         else:
 | |
|             commits = None
 | |
| 
 | |
|         Print(GetActionSummary(options.summary, commits, board_selected,
 | |
|                                 options))
 | |
| 
 | |
|         # We can't show function sizes without board details at present
 | |
|         if options.show_bloat:
 | |
|             options.show_detail = True
 | |
|         builder.SetDisplayOptions(options.show_errors, options.show_sizes,
 | |
|                                   options.show_detail, options.show_bloat,
 | |
|                                   options.list_error_boards,
 | |
|                                   options.show_config,
 | |
|                                   options.show_environment)
 | |
|         if options.summary:
 | |
|             builder.ShowSummary(commits, board_selected)
 | |
|         else:
 | |
|             fail, warned = builder.BuildBoards(commits, board_selected,
 | |
|                                 options.keep_outputs, options.verbose)
 | |
|             if fail:
 | |
|                 return 128
 | |
|             elif warned:
 | |
|                 return 129
 | |
|     return 0
 |