binman: Convert to use ArgumentParser
[oweals/u-boot.git] / tools / binman / control.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
4 #
5 # Creates binary images from input files controlled by a description
6 #
7
8 from __future__ import print_function
9
10 from collections import OrderedDict
11 import os
12 import sys
13 import tools
14
15 import cbfs_util
16 import command
17 import elf
18 from image import Image
19 import state
20 import tout
21
22 # List of images we plan to create
23 # Make this global so that it can be referenced from tests
24 images = OrderedDict()
25
26 def _ReadImageDesc(binman_node):
27     """Read the image descriptions from the /binman node
28
29     This normally produces a single Image object called 'image'. But if
30     multiple images are present, they will all be returned.
31
32     Args:
33         binman_node: Node object of the /binman node
34     Returns:
35         OrderedDict of Image objects, each of which describes an image
36     """
37     images = OrderedDict()
38     if 'multiple-images' in binman_node.props:
39         for node in binman_node.subnodes:
40             images[node.name] = Image(node.name, node)
41     else:
42         images['image'] = Image('image', binman_node)
43     return images
44
45 def _FindBinmanNode(dtb):
46     """Find the 'binman' node in the device tree
47
48     Args:
49         dtb: Fdt object to scan
50     Returns:
51         Node object of /binman node, or None if not found
52     """
53     for node in dtb.GetRoot().subnodes:
54         if node.name == 'binman':
55             return node
56     return None
57
58 def WriteEntryDocs(modules, test_missing=None):
59     """Write out documentation for all entries
60
61     Args:
62         modules: List of Module objects to get docs for
63         test_missing: Used for testing only, to force an entry's documeentation
64             to show as missing even if it is present. Should be set to None in
65             normal use.
66     """
67     from entry import Entry
68     Entry.WriteDocs(modules, test_missing)
69
70 def Binman(args):
71     """The main control code for binman
72
73     This assumes that help and test options have already been dealt with. It
74     deals with the core task of building images.
75
76     Args:
77         args: Command line arguments Namespace object
78     """
79     global images
80
81     if args.full_help:
82         pager = os.getenv('PAGER')
83         if not pager:
84             pager = 'more'
85         fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
86                             'README')
87         command.Run(pager, fname)
88         return 0
89
90     # Try to figure out which device tree contains our image description
91     if args.dt:
92         dtb_fname = args.dt
93     else:
94         board = args.board
95         if not board:
96             raise ValueError('Must provide a board to process (use -b <board>)')
97         board_pathname = os.path.join(args.build_dir, board)
98         dtb_fname = os.path.join(board_pathname, 'u-boot.dtb')
99         if not args.indir:
100             args.indir = ['.']
101         args.indir.append(board_pathname)
102
103     try:
104         # Import these here in case libfdt.py is not available, in which case
105         # the above help option still works.
106         import fdt
107         import fdt_util
108
109         tout.Init(args.verbosity)
110         elf.debug = args.debug
111         cbfs_util.VERBOSE = args.verbosity > 2
112         state.use_fake_dtb = args.fake_dtb
113         try:
114             tools.SetInputDirs(args.indir)
115             tools.PrepareOutputDir(args.outdir, args.preserve)
116             tools.SetToolPaths(args.toolpath)
117             state.SetEntryArgs(args.entry_arg)
118
119             # Get the device tree ready by compiling it and copying the compiled
120             # output into a file in our output directly. Then scan it for use
121             # in binman.
122             dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
123             fname = tools.GetOutputFilename('u-boot.dtb.out')
124             tools.WriteFile(fname, tools.ReadFile(dtb_fname))
125             dtb = fdt.FdtScan(fname)
126
127             node = _FindBinmanNode(dtb)
128             if not node:
129                 raise ValueError("Device tree '%s' does not have a 'binman' "
130                                  "node" % dtb_fname)
131
132             images = _ReadImageDesc(node)
133
134             if args.image:
135                 skip = []
136                 new_images = OrderedDict()
137                 for name, image in images.items():
138                     if name in args.image:
139                         new_images[name] = image
140                     else:
141                         skip.append(name)
142                 images = new_images
143                 if skip and args.verbosity >= 2:
144                     print('Skipping images: %s' % ', '.join(skip))
145
146             state.Prepare(images, dtb)
147
148             # Prepare the device tree by making sure that any missing
149             # properties are added (e.g. 'pos' and 'size'). The values of these
150             # may not be correct yet, but we add placeholders so that the
151             # size of the device tree is correct. Later, in
152             # SetCalculatedProperties() we will insert the correct values
153             # without changing the device-tree size, thus ensuring that our
154             # entry offsets remain the same.
155             for image in images.values():
156                 image.ExpandEntries()
157                 if args.update_fdt:
158                     image.AddMissingProperties()
159                 image.ProcessFdt(dtb)
160
161             for dtb_item in state.GetFdts():
162                 dtb_item.Sync(auto_resize=True)
163                 dtb_item.Pack()
164                 dtb_item.Flush()
165
166             for image in images.values():
167                 # Perform all steps for this image, including checking and
168                 # writing it. This means that errors found with a later
169                 # image will be reported after earlier images are already
170                 # completed and written, but that does not seem important.
171                 image.GetEntryContents()
172                 image.GetEntryOffsets()
173                 try:
174                     image.PackEntries()
175                     image.CheckSize()
176                     image.CheckEntries()
177                 except Exception as e:
178                     if args.map:
179                         fname = image.WriteMap()
180                         print("Wrote map file '%s' to show errors"  % fname)
181                     raise
182                 image.SetImagePos()
183                 if args.update_fdt:
184                     image.SetCalculatedProperties()
185                     for dtb_item in state.GetFdts():
186                         dtb_item.Sync()
187                 image.ProcessEntryContents()
188                 image.WriteSymbols()
189                 image.BuildImage()
190                 if args.map:
191                     image.WriteMap()
192
193             # Write the updated FDTs to our output files
194             for dtb_item in state.GetFdts():
195                 tools.WriteFile(dtb_item._fname, dtb_item.GetContents())
196
197         finally:
198             tools.FinaliseOutputDir()
199     finally:
200         tout.Uninit()
201
202     return 0