Merge tag 'efi-2020-01-rc1' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
[oweals/u-boot.git] / tools / buildman / control.py
index 8b3cd30c00119aa990cc0dcc22aa627aa567b400..fcf531c5f143e8062b941e35a0669f8d9b7034d8 100644 (file)
@@ -1,7 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
 # Copyright (c) 2013 The Chromium OS Authors.
 #
-# SPDX-License-Identifier:     GPL-2.0+
-#
 
 import multiprocessing
 import os
@@ -42,20 +41,22 @@ def GetActionSummary(is_summary, commits, selected, options):
             GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
     return str
 
-def ShowActions(series, why_selected, boards_selected, builder, options):
+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 boards brought
-                in by that argument. For example, 'arm' might bring in
-                400 boards, so in this case the key would be 'arm' and
+                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:'
@@ -75,9 +76,36 @@ def ShowActions(series, why_selected, boards_selected, builder, options):
     print
     for arg in why_selected:
         if arg != 'all':
-            print arg, ': %d boards' % why_selected[arg]
+            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' %
-            why_selected['all'])
+            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):
@@ -101,43 +129,50 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
         pager = os.getenv('PAGER')
         if not pager:
             pager = 'more'
-        fname = os.path.join(os.path.dirname(sys.argv[0]), 'README')
+        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')
 
-    if not toolchains:
-        toolchains = toolchain.Toolchains()
-        toolchains.GetSettings()
-        toolchains.Scan(options.list_tool_chains)
-    if options.list_tool_chains:
-        toolchains.List()
-        print
-        return 0
+    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 'Available architectures: %s\n' % ' '.join(sorted_list)
+            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 'Downloading toolchains: %s\n' % fetch_arch
+                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.
-    col = terminal.Color()
     count = options.count
     has_range = options.branch and '..' in options.branch
     if count == -1:
@@ -180,7 +215,15 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
         for arg in options.exclude:
             exclude += arg.split(',')
 
-    why_selected = boards.SelectBoards(args, exclude)
+
+    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'))
@@ -214,9 +257,10 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
                     options.git_dir, count, series=None, allow_overwrite=True)
     else:
         series = None
-        options.verbose = True
-        if not options.summary:
-            options.show_errors = True
+        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.
@@ -230,7 +274,7 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
         options.step = len(series.commits) - 1
 
     gnu_make = command.Output(os.path.join(options.git,
-                                           'scripts/show-gnu-make')).rstrip()
+            'scripts/show-gnu-make'), raise_on_error=False).rstrip()
     if not gnu_make:
         sys.exit('GNU Make not found')
 
@@ -242,21 +286,27 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
         # 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 output_dir != options.output_dir and
-            os.path.exists(output_dir)):
-        shutil.rmtree(output_dir)
+        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)
+            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)
+        ShowActions(series, why_selected, selected, builder, options,
+                    board_warnings)
     else:
         builder.force_build = options.force_build
         builder.force_build_failures = options.force_build_failures
@@ -283,7 +333,8 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
         builder.SetDisplayOptions(options.show_errors, options.show_sizes,
                                   options.show_detail, options.show_bloat,
                                   options.list_error_boards,
-                                  options.show_config)
+                                  options.show_config,
+                                  options.show_environment)
         if options.summary:
             builder.ShowSummary(commits, board_selected)
         else: