blob: 1df8f2ecd260215452f893fb5eaed127e0b30191 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass1f1864b2016-07-25 18:59:08 -06002#
3# Copyright (c) 2016 Google, Inc
4#
Simon Glass1f1864b2016-07-25 18:59:08 -06005
Simon Glassaeffc5e2018-07-17 13:25:43 -06006import command
Simon Glass0a98b282018-09-14 04:57:28 -06007import glob
Simon Glass1f1864b2016-07-25 18:59:08 -06008import os
9import shutil
10import tempfile
11
12import tout
13
Simon Glassaeffc5e2018-07-17 13:25:43 -060014# Output directly (generally this is temporary)
Simon Glass1f1864b2016-07-25 18:59:08 -060015outdir = None
Simon Glassaeffc5e2018-07-17 13:25:43 -060016
17# True to keep the output directory around after exiting
Simon Glass1f1864b2016-07-25 18:59:08 -060018preserve_outdir = False
19
Simon Glassaeffc5e2018-07-17 13:25:43 -060020# 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
Simon Glass04187a82018-09-14 04:57:25 -060026# Tools and the packages that contain them, on debian
27packages = {
28 'lz4': 'liblz4-tool',
29 }
Simon Glassaeffc5e2018-07-17 13:25:43 -060030
Simon Glass1fda1822018-10-01 21:12:44 -060031# List of paths to use when looking for an input file
32indir = []
33
Simon Glass1f1864b2016-07-25 18:59:08 -060034def 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
Simon Glass4f5dea42018-07-17 13:25:45 -0600127 raise ValueError("Filename '%s' not found in input path (%s) (cwd='%s')" %
128 (fname, ','.join(indir), os.getcwd()))
Simon Glass1f1864b2016-07-25 18:59:08 -0600129
Simon Glass0a98b282018-09-14 04:57:28 -0600130def 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
Simon Glass1f1864b2016-07-25 18:59:08 -0600147def 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))
Simon Glassaeffc5e2018-07-17 13:25:43 -0600155
Simon Glass04187a82018-09-14 04:57:25 -0600156def 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
Simon Glassa92939a2019-05-14 15:53:44 -0600170def Run(name, *args, **kwargs):
Simon Glass04187a82018-09-14 04:57:25 -0600171 try:
Simon Glassa92939a2019-05-14 15:53:44 -0600172 return command.Run(name, *args, cwd=outdir, capture=True, **kwargs)
Simon Glass04187a82018-09-14 04:57:25 -0600173 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
Simon Glassaeffc5e2018-07-17 13:25:43 -0600181
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)