xref: /openbmc/u-boot/tools/patman/tools.py (revision cf033e04)
1# SPDX-License-Identifier: GPL-2.0+
2#
3# Copyright (c) 2016 Google, Inc
4#
5
6import command
7import glob
8import os
9import shutil
10import tempfile
11
12import tout
13
14# Output directly (generally this is temporary)
15outdir = None
16
17# True to keep the output directory around after exiting
18preserve_outdir = False
19
20# Path to the Chrome OS chroot, if we know it
21chroot_path = None
22
23# Search paths to use for Filename(), used to find files
24search_paths = []
25
26# Tools and the packages that contain them, on debian
27packages = {
28    'lz4': 'liblz4-tool',
29    }
30
31# List of paths to use when looking for an input file
32indir = []
33
34def PrepareOutputDir(dirname, preserve=False):
35    """Select an output directory, ensuring it exists.
36
37    This either creates a temporary directory or checks that the one supplied
38    by the user is valid. For a temporary directory, it makes a note to
39    remove it later if required.
40
41    Args:
42        dirname: a string, name of the output directory to use to store
43                intermediate and output files. If is None - create a temporary
44                directory.
45        preserve: a Boolean. If outdir above is None and preserve is False, the
46                created temporary directory will be destroyed on exit.
47
48    Raises:
49        OSError: If it cannot create the output directory.
50    """
51    global outdir, preserve_outdir
52
53    preserve_outdir = dirname or preserve
54    if dirname:
55        outdir = dirname
56        if not os.path.isdir(outdir):
57            try:
58                os.makedirs(outdir)
59            except OSError as err:
60                raise CmdError("Cannot make output directory '%s': '%s'" %
61                                (outdir, err.strerror))
62        tout.Debug("Using output directory '%s'" % outdir)
63    else:
64        outdir = tempfile.mkdtemp(prefix='binman.')
65        tout.Debug("Using temporary directory '%s'" % outdir)
66
67def _RemoveOutputDir():
68    global outdir
69
70    shutil.rmtree(outdir)
71    tout.Debug("Deleted temporary directory '%s'" % outdir)
72    outdir = None
73
74def FinaliseOutputDir():
75    global outdir, preserve_outdir
76
77    """Tidy up: delete output directory if temporary and not preserved."""
78    if outdir and not preserve_outdir:
79        _RemoveOutputDir()
80
81def GetOutputFilename(fname):
82    """Return a filename within the output directory.
83
84    Args:
85        fname: Filename to use for new file
86
87    Returns:
88        The full path of the filename, within the output directory
89    """
90    return os.path.join(outdir, fname)
91
92def _FinaliseForTest():
93    """Remove the output directory (for use by tests)"""
94    global outdir
95
96    if outdir:
97        _RemoveOutputDir()
98
99def SetInputDirs(dirname):
100    """Add a list of input directories, where input files are kept.
101
102    Args:
103        dirname: a list of paths to input directories to use for obtaining
104                files needed by binman to place in the image.
105    """
106    global indir
107
108    indir = dirname
109    tout.Debug("Using input directories %s" % indir)
110
111def GetInputFilename(fname):
112    """Return a filename for use as input.
113
114    Args:
115        fname: Filename to use for new file
116
117    Returns:
118        The full path of the filename, within the input directory
119    """
120    if not indir:
121        return fname
122    for dirname in indir:
123        pathname = os.path.join(dirname, fname)
124        if os.path.exists(pathname):
125            return pathname
126
127    raise ValueError("Filename '%s' not found in input path (%s) (cwd='%s')" %
128                     (fname, ','.join(indir), os.getcwd()))
129
130def GetInputFilenameGlob(pattern):
131    """Return a list of filenames for use as input.
132
133    Args:
134        pattern: Filename pattern to search for
135
136    Returns:
137        A list of matching files in all input directories
138    """
139    if not indir:
140        return glob.glob(fname)
141    files = []
142    for dirname in indir:
143        pathname = os.path.join(dirname, pattern)
144        files += glob.glob(pathname)
145    return sorted(files)
146
147def Align(pos, align):
148    if align:
149        mask = align - 1
150        pos = (pos + mask) & ~mask
151    return pos
152
153def NotPowerOfTwo(num):
154    return num and (num & (num - 1))
155
156def PathHasFile(fname):
157    """Check if a given filename is in the PATH
158
159    Args:
160        fname: Filename to check
161
162    Returns:
163        True if found, False if not
164    """
165    for dir in os.environ['PATH'].split(':'):
166        if os.path.exists(os.path.join(dir, fname)):
167            return True
168    return False
169
170def Run(name, *args):
171    try:
172        return command.Run(name, *args, cwd=outdir, capture=True)
173    except:
174        if not PathHasFile(name):
175            msg = "Plesae install tool '%s'" % name
176            package = packages.get(name)
177            if package:
178                 msg += " (e.g. from package '%s')" % package
179            raise ValueError(msg)
180        raise
181
182def Filename(fname):
183    """Resolve a file path to an absolute path.
184
185    If fname starts with ##/ and chroot is available, ##/ gets replaced with
186    the chroot path. If chroot is not available, this file name can not be
187    resolved, `None' is returned.
188
189    If fname is not prepended with the above prefix, and is not an existing
190    file, the actual file name is retrieved from the passed in string and the
191    search_paths directories (if any) are searched to for the file. If found -
192    the path to the found file is returned, `None' is returned otherwise.
193
194    Args:
195      fname: a string,  the path to resolve.
196
197    Returns:
198      Absolute path to the file or None if not found.
199    """
200    if fname.startswith('##/'):
201      if chroot_path:
202        fname = os.path.join(chroot_path, fname[3:])
203      else:
204        return None
205
206    # Search for a pathname that exists, and return it if found
207    if fname and not os.path.exists(fname):
208        for path in search_paths:
209            pathname = os.path.join(path, os.path.basename(fname))
210            if os.path.exists(pathname):
211                return pathname
212
213    # If not found, just return the standard, unchanged path
214    return fname
215
216def ReadFile(fname):
217    """Read and return the contents of a file.
218
219    Args:
220      fname: path to filename to read, where ## signifiies the chroot.
221
222    Returns:
223      data read from file, as a string.
224    """
225    with open(Filename(fname), 'rb') as fd:
226        data = fd.read()
227    #self._out.Info("Read file '%s' size %d (%#0x)" %
228                   #(fname, len(data), len(data)))
229    return data
230
231def WriteFile(fname, data):
232    """Write data into a file.
233
234    Args:
235        fname: path to filename to write
236        data: data to write to file, as a string
237    """
238    #self._out.Info("Write file '%s' size %d (%#0x)" %
239                   #(fname, len(data), len(data)))
240    with open(Filename(fname), 'wb') as fd:
241        fd.write(data)
242