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