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