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