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