1# Copyright (C) 2014-2018 Intel Corporation. All Rights Reserved. 2# 3# Permission is hereby granted, free of charge, to any person obtaining a 4# copy of this software and associated documentation files (the "Software"), 5# to deal in the Software without restriction, including without limitation 6# the rights to use, copy, modify, merge, publish, distribute, sublicense, 7# and/or sell copies of the Software, and to permit persons to whom the 8# Software is furnished to do so, subject to the following conditions: 9# 10# The above copyright notice and this permission notice (including the next 11# paragraph) shall be included in all copies or substantial portions of the 12# Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20# IN THE SOFTWARE. 21 22# Python source 23import os 24import errno 25import sys 26import argparse 27import tempfile 28import filecmp 29import shutil 30from mako.template import Template 31from mako.exceptions import RichTraceback 32 33#============================================================================== 34def ConcatLists(list_of_lists): 35 output = [] 36 for l in list_of_lists: output += l 37 return output 38 39#============================================================================== 40def MakeTmpDir(suffix=''): 41 ''' 42 Create temporary directory for use in codegen scripts. 43 ''' 44 return tempfile.mkdtemp(suffix) 45 46#============================================================================== 47def MakeDir(dir_path): 48 ''' 49 Create a directory if it doesn't exist 50 51 returns 0 on success, non-zero on failure 52 ''' 53 dir_path = os.path.abspath(dir_path) 54 55 if not os.path.exists(dir_path): 56 try: 57 os.makedirs(dir_path) 58 except OSError as err: 59 if err.errno != errno.EEXIST: 60 return 1 61 else: 62 if not os.path.isdir(dir_path): 63 return 1 64 65 return 0 66 67#============================================================================== 68def DeleteDirTree(dir_path): 69 ''' 70 Delete directory tree. 71 72 returns 0 on success, non-zero on failure 73 ''' 74 rval = 0 75 try: 76 shutil.rmtree(dir_path, False) 77 except: 78 rval = 1 79 return rval 80 81#============================================================================== 82def CopyFileIfDifferent(src, dst, verbose = False): 83 ''' 84 Copy <src> file to <dst> file if the <dst> 85 file either doesn't contain the file or the file 86 contents are different. 87 88 returns 0 on success, non-zero on failure 89 ''' 90 91 assert os.path.isfile(src) 92 assert (False == os.path.exists(dst) or os.path.isfile(dst)) 93 94 need_copy = not os.path.exists(dst) 95 if not need_copy: 96 need_copy = not filecmp.cmp(src, dst) 97 98 if need_copy: 99 try: 100 shutil.copy2(src, dst) 101 except: 102 print('ERROR: Could not copy %s to %s' % (src, dst), file=sys.stderr) 103 return 1 104 105 if verbose: 106 print(src, '-->', dst) 107 108 return 0 109 110#============================================================================== 111def CopyDirFilesIfDifferent(src, dst, recurse = True, verbose = False, orig_dst = None): 112 ''' 113 Copy files <src> directory to <dst> directory if the <dst> 114 directory either doesn't contain the file or the file 115 contents are different. 116 117 Optionally recurses into subdirectories 118 119 returns 0 on success, non-zero on failure 120 ''' 121 122 assert os.path.isdir(src) 123 assert os.path.isdir(dst) 124 125 src = os.path.abspath(src) 126 dst = os.path.abspath(dst) 127 128 if not orig_dst: 129 orig_dst = dst 130 131 for f in os.listdir(src): 132 src_path = os.path.join(src, f) 133 dst_path = os.path.join(dst, f) 134 135 # prevent recursion 136 if src_path == orig_dst: 137 continue 138 139 if os.path.isdir(src_path): 140 if recurse: 141 if MakeDir(dst_path): 142 print('ERROR: Could not create directory:', dst_path, file=sys.stderr) 143 return 1 144 145 if verbose: 146 print('mkdir', dst_path) 147 rval = CopyDirFilesIfDifferent(src_path, dst_path, recurse, verbose, orig_dst) 148 else: 149 rval = CopyFileIfDifferent(src_path, dst_path, verbose) 150 151 if rval: 152 return rval 153 154 return 0 155 156#============================================================================== 157class MakoTemplateWriter: 158 ''' 159 MakoTemplateWriter - Class (namespace) for functions to generate strings 160 or files using the Mako template module. 161 162 See http://docs.makotemplates.org/en/latest/ for 163 mako documentation. 164 ''' 165 166 @staticmethod 167 def to_string(template_filename, **kwargs): 168 ''' 169 Write template data to a string object and return the string 170 ''' 171 from mako.template import Template 172 from mako.exceptions import RichTraceback 173 174 try: 175 template = Template(filename=template_filename) 176 # Split + Join fixes line-endings for whatever platform you are using 177 return '\n'.join(template.render(**kwargs).splitlines()) 178 except: 179 traceback = RichTraceback() 180 for (filename, lineno, function, line) in traceback.traceback: 181 print('File %s, line %s, in %s' % (filename, lineno, function)) 182 print(line, '\n') 183 print('%s: %s' % (str(traceback.error.__class__.__name__), traceback.error)) 184 raise 185 186 @staticmethod 187 def to_file(template_filename, output_filename, **kwargs): 188 ''' 189 Write template data to a file 190 ''' 191 if MakeDir(os.path.dirname(output_filename)): 192 return 1 193 with open(output_filename, 'w') as outfile: 194 print(MakoTemplateWriter.to_string(template_filename, **kwargs), file=outfile) 195 return 0 196 197 198#============================================================================== 199class ArgumentParser(argparse.ArgumentParser): 200 ''' 201 Subclass of argparse.ArgumentParser 202 203 Allow parsing from command files that start with @ 204 Example: 205 >bt run @myargs.txt 206 207 Contents of myargs.txt: 208 -m <machine> 209 --target cdv_win7 210 211 The below function allows multiple args to be placed on the same text-file line. 212 The default is one token per line, which is a little cumbersome. 213 214 Also allow all characters after a '#' character to be ignored. 215 ''' 216 217 #============================================================================== 218 class _HelpFormatter(argparse.RawTextHelpFormatter): 219 ''' Better help formatter for argument parser ''' 220 221 def _split_lines(self, text, width): 222 ''' optimized split lines algorithm, indents split lines ''' 223 lines = text.splitlines() 224 out_lines = [] 225 if len(lines): 226 out_lines.append(lines[0]) 227 for line in lines[1:]: 228 out_lines.append(' ' + line) 229 return out_lines 230 231 #============================================================================== 232 def __init__(self, *args, **kwargs): 233 ''' Constructor. Compatible with argparse.ArgumentParser(), 234 but with some modifications for better usage and help display. 235 ''' 236 super(ArgumentParser, self).__init__( 237 *args, 238 fromfile_prefix_chars='@', 239 formatter_class=ArgumentParser._HelpFormatter, 240 **kwargs) 241 242 #========================================================================== 243 def convert_arg_line_to_args(self, arg_line): 244 ''' convert one line of parsed file to arguments ''' 245 arg_line = arg_line.split('#', 1)[0] 246 if sys.platform == 'win32': 247 arg_line = arg_line.replace('\\', '\\\\') 248 for arg in shlex.split(arg_line): 249 if not arg.strip(): 250 continue 251 yield arg 252 253 #========================================================================== 254 def _read_args_from_files(self, arg_strings): 255 ''' read arguments from files ''' 256 # expand arguments referencing files 257 new_arg_strings = [] 258 for arg_string in arg_strings: 259 260 # for regular arguments, just add them back into the list 261 if arg_string[0] not in self.fromfile_prefix_chars: 262 new_arg_strings.append(arg_string) 263 264 # replace arguments referencing files with the file content 265 else: 266 filename = arg_string[1:] 267 268 # Search in sys.path 269 if not os.path.exists(filename): 270 for path in sys.path: 271 filename = os.path.join(path, arg_string[1:]) 272 if os.path.exists(filename): 273 break 274 275 try: 276 args_file = open(filename) 277 try: 278 arg_strings = [] 279 for arg_line in args_file.read().splitlines(): 280 for arg in self.convert_arg_line_to_args(arg_line): 281 arg_strings.append(arg) 282 arg_strings = self._read_args_from_files(arg_strings) 283 new_arg_strings.extend(arg_strings) 284 finally: 285 args_file.close() 286 except IOError: 287 err = sys.exc_info()[1] 288 self.error(str(err)) 289 290 # return the modified argument list 291 return new_arg_strings 292