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