Package mbuild :: Module util
[frames] | no frames]

Source Code for Module mbuild.util

   1  # -*- python -*- 
   2  # Mark Charney  
   3  #BEGIN_LEGAL 
   4  # 
   5  #Copyright (c) 2016 Intel Corporation 
   6  # 
   7  #  Licensed under the Apache License, Version 2.0 (the "License"); 
   8  #  you may not use this file except in compliance with the License. 
   9  #  You may obtain a copy of the License at 
  10  # 
  11  #      http://www.apache.org/licenses/LICENSE-2.0 
  12  # 
  13  #  Unless required by applicable law or agreed to in writing, software 
  14  #  distributed under the License is distributed on an "AS IS" BASIS, 
  15  #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  16  #  See the License for the specific language governing permissions and 
  17  #  limitations under the License. 
  18  #   
  19  #END_LEGAL 
  20   
  21  """Basic useful utilities: file copying, removal, permissions, 
  22  path-name manipulation, and command execution.""" 
  23   
  24  import os 
  25  import re 
  26  import glob 
  27  import sys 
  28  import shutil 
  29  import stat 
  30  import types 
  31  import time 
  32  import subprocess 
  33  import tempfile 
  34  import shlex 
  35  import mbuild 
  36  import traceback 
  37  try: 
  38      import cPickle as apickle 
  39  except: 
  40      import pickle as apickle 
  41   
  42  from base import * 
  43   
44 -def find_python(env):
45 """return path to NON cygwin""" 46 pycmd = sys.executable # use whatever the user invoked us with 47 if env.on_windows() and env.on_cygwin(): 48 # avoid cygwin python 49 if pycmd in ['/usr/bin/python', '/bin/python']: 50 python_commands = [ 'c:/python27/python.exe', 51 'c:/python26/python.exe', 52 'c:/python25/python.exe' ] 53 pycmd = None 54 for p in python_commands: 55 if os.path.exists(p): 56 return p 57 if not pycmd: 58 mbuild.die("Could not find win32 python at these locations: %s" % 59 "\n\t" + "\n\t".join(python_commands)) 60 61 return pycmd
62 -def copy_file(src,tgt):
63 """Copy src to tgt.""" 64 if verbose(1): 65 msgb("COPY", tgt + " <- " + src) 66 shutil.copy(src,tgt)
67 -def move_file(src,tgt):
68 """Move/Rename src to tgt.""" 69 if verbose(1): 70 msgb("MOVE", src + " -> " + tgt) 71 shutil.move(src,tgt)
79
80 -def copy_tree(src,tgt, ignore_patterns=None, symlinks=False):
81 """Copy the tree at src to tgt. This will first remove tgt if it 82 already exists.""" 83 if verbose(1): 84 msgb("COPYTREE", tgt + " <- " + src) 85 if not os.path.exists(src): 86 error_msg("SRC TREE DOES NOT EXIST", src) 87 raise Exception 88 if os.path.exists(tgt): 89 if verbose(1): 90 msgb("Removing existing target tree", tgt) 91 shutil.rmtree(tgt, ignore_errors=True) 92 if verbose(1): 93 msgb("Copying to tree", tgt) 94 if ignore_patterns: 95 sp = shutil.ignore_patterns(ignore_patterns) 96 else: 97 sp = None 98 shutil.copytree(src,tgt,ignore=sp, symlinks=symlinks) 99 if verbose(1): 100 msgb("Done copying tree", tgt)
101
102 -def cmkdir(path_to_dir):
103 """Make a directory if it does not exist""" 104 if not os.path.exists(path_to_dir): 105 if verbose(1): 106 msgb("MKDIR", path_to_dir) 107 os.makedirs(path_to_dir)
108 -def list2string(ls):
109 """Print a list as a string""" 110 s = " ".join(ls) 111 return s
112
113 -def remove_file(fn, env=None, quiet=True):
114 """Remove a file or link if it exists. env parameter is not used.""" 115 if os.path.exists(fn): 116 make_writable(fn) 117 if os.path.exists(fn) or os.path.lexists(fn): 118 if not quiet: 119 if verbose(1): 120 msgb("REMOVING", fn) 121 os.unlink(fn) 122 return (0, [])
123 -def remove_tree(dir_name, env=None, dangerous=False):
124 """Remove a directory if it exists. env parameter is not 125 used. This will not remove a directory that has a .svn 126 subdirectory indicating it is a source directory. Warning: It does 127 not look recursively for .svn subdirectories. 128 @type dir_name: string 129 @param dir_name: a directory name 130 @type env: L{env_t} 131 @param env: optional. Not currently used. 132 @type dangerous: bool 133 @param dangerous: optional. If True,will delete anything including svn trees!! BE CAREFUL! default False. 134 """ 135 if verbose(1): 136 msgb("CHECKING", dir_name) 137 if os.path.exists(dir_name): 138 if not dangerous and os.path.exists(os.path.join(dir_name, ".svn")): 139 s = 'Did not remove directory %s because of a .svn subdirectory' % \ 140 dir_name 141 warn(s) 142 return (1, [ s ]) 143 if verbose(1): 144 msgb("REMOVING", dir_name) 145 make_writable(dir_name) 146 shutil.rmtree(dir_name, ignore_errors = True) 147 return (0, [])
148 -def remove_files(lst, env=None):
149 """Remove all the files in the list of files, lst. The env 150 parameter is not used""" 151 for fn in lst: 152 remove_file(fn) 153 return (0, [])
154
155 -def remove_files_glob(lst,env=None):
156 """Remove all files in the list of wild card expressions. The env 157 parameter is not used""" 158 for fn_glob in lst: 159 #msgb("REMOVING", fn_glob) 160 for file_name in glob(fn_glob): 161 remove_file(file_name) 162 return (0, [])
163
164 -def remove_files_from_tree(dir, file_patterns):
165 """Remove files that match the re object compiled pattern provided""" 166 for (dir, subdirs, subfiles) in os.walk(dir): 167 for file_name in subfiles: 168 fn = os.path.join(dir,file_name) 169 if file_patterns.search(fn): 170 remove_file(fn)
171 172 173 _readable_by_all = stat.S_IRUSR|stat.S_IRGRP|stat.S_IROTH 174 _readable_by_ug = stat.S_IRUSR|stat.S_IRGRP 175 _executable_by_all = stat.S_IXUSR|stat.S_IXGRP|stat.S_IXOTH 176 _executable_by_ug = stat.S_IXUSR|stat.S_IXGRP 177 _writeable_by_me = stat.S_IWUSR 178 _rwx_by_me = stat.S_IWUSR| stat.S_IRUSR|stat.S_IXUSR 179 _writeable_by_ug = stat.S_IWUSR|stat.S_IWGRP 180
181 -def make_writable(fn):
182 """Make the file or directory readable/writable/executable by me""" 183 global _rwx_by_me 184 os.chmod(fn, _rwx_by_me)
185
186 -def make_executable(fn):
187 """Make the file or directory readable & executable by user/group, writable by user""" 188 global _executable_by_ug 189 global _readable_by_ug 190 global _writeable_by_me 191 os.chmod(fn, _readable_by_ug|_writeable_by_me|_executable_by_ug)
192
193 -def modify_dir_tree(path, dir_fn=None, file_fn=None):
194 """Walk the tree rooted at path and apply the function dir_fn to 195 directories and file_fn to files. This is intended for doing 196 recursive chmods, etc.""" 197 if dir_fn: 198 dir_fn(path) 199 for (dir, subdirs, subfiles) in os.walk(path): 200 if dir_fn: 201 for subdir in subdirs: 202 dir_fn(os.path.join(dir,subdir)) 203 if file_fn: 204 for file_name in subfiles: 205 file_fn(os.path.join(dir,file_name))
206 207
208 -def make_read_only(fn):
209 """Make the file fn read-only""" 210 global _readable_by_all 211 os.chmod(fn, _readable_by_all)
212
213 -def make_web_accessible(fn):
214 """Make the file readable by all and writable by the current owner""" 215 global _readable_by_all 216 global _writeable_by_me 217 if verbose(8): 218 msgb("make_web_accessible", fn) 219 os.chmod(fn, _writeable_by_me|_readable_by_all)
220 -def make_web_accessible_dir(dir):
221 """Make the directory readable and executable by all and writable 222 by the current owner""" 223 global _readable_by_all 224 global _executable_by_all 225 global _writeable_by_me 226 if verbose(8): 227 msgb("make_web_accessible_dir", dir) 228 os.chmod(dir, _writeable_by_me|_readable_by_all|_executable_by_all)
229
230 -def make_documentation_tree_accessible(dir):
231 """Make the directory teree rooted at dir web-accessible. That is, 232 the directories are readable and executable by anyone and the 233 files are readable by anyone.""" 234 msgb("CHMOD TREE", dir) 235 modify_dir_tree(dir, make_web_accessible_dir, make_web_accessible)
236 237 238
239 -def prefix_files(dir,input_files):
240 """Add dir on to the front of the input file or files. Works with 241 strings or lists of strings. 242 @type dir: string 243 @param dir: prefix directory 244 245 @type input_files: string or list of strings 246 @param input_files: name(s) of files 247 248 @rtype: string or list of strings 249 @return: input file(s) prefixed with dir sp 250 """ 251 if isinstance(input_files,types.ListType): 252 new_files = map(lambda(x): join(dir, x), input_files) 253 return new_files 254 elif isinstance(input_files,types.StringType): 255 new_file = join(dir, input_files) 256 return new_file 257 die("Unhandled type in prefix_files: "+ str(type(input_files)))
258
259 -def quote(fn):
260 """Add quotes around the file nameed fn. Return a string""" 261 return "\"%s\"" % fn
262
263 -def qdip(fn):
264 """Add quotes to a string if there are spaces in the name""" 265 if re.search(' ',fn): 266 return '"%s"' % fn 267 return fn
268 269
270 -def touch(fn):
271 """Open a file for append. Write nothing to it""" 272 if verbose(): 273 msgb("TOUCH", fn) 274 f=open(fn,"a") 275 f.close()
276 277 ############################################################ 278 if on_native_windows(): 279 _mysep = "\\" 280 else: 281 _mysep = "/" 282
283 -def myjoin( *args ):
284 """join all the args supplied as arguments using _mysep as the 285 separator. _mysep is a backslash on native windows and a forward 286 slash everywhere else. 287 @type args: strings 288 @param args: path component strings 289 290 @rtype: string 291 @return: string with _mysep slashes 292 """ 293 s = '' 294 first = True 295 for a in args: 296 if first: 297 first = False 298 else: 299 s = s + _mysep 300 s = s + a 301 return s
302
303 -def strip_quotes(a):
304 """Conditionally remove leading/trailing quotes from a string 305 @type a: string 306 @param a: a string potentially with quotes 307 308 @rtype: string 309 @return: same string without the leading and trailing quotes 310 """ 311 ln = len(a) 312 if ln >= 2: 313 strip_quotes = False 314 if a[0] == '"' and a[-1] == '"': 315 strip_quotes=True 316 elif a[0] == "'" and a[-1] == "'": 317 strip_quotes=True 318 if strip_quotes: 319 b = a[1:ln-1] 320 return b 321 return a
322
323 -def join( *args ):
324 """join all the args supplied as arguments using a forward slash as 325 the separator 326 327 @type args: strings 328 @param args: path component strings 329 330 @rtype: string 331 @return: string with forward-slashes 332 """ 333 s = '' 334 first = True 335 for a in args: 336 ln = len(s) 337 if first: 338 first = False 339 elif ln == 0 or s[-1] != '/': 340 # if the last character is not a fwd slash already, add a slash 341 s = s + '/' 342 a = strip_quotes(a) 343 s = s + a 344 return s
345 346
347 -def flip_slashes(s):
348 """convert to backslashes to _mysep slashes. _mysep slashes are 349 defined to be backslashes on native windows and forward slashes 350 everywhere else. 351 @type s: string or list of strings 352 @param s: path name(s) 353 354 @rtype: string or list of strings 355 @return: string(s) with _mysep slashes 356 """ 357 358 if on_native_windows(): 359 return s 360 if type(s) == types.ListType: 361 return map(flip_slashes, s) 362 t = re.sub(r'\\',_mysep,s,0) # replace all 363 return t
364
365 -def posix_slashes(s):
366 """convert to posix slashes. Do not flip slashes immediately before spaces 367 @type s: string or list of strings 368 @param s: path name(s) 369 370 @rtype: string or list of strings 371 @return: string(s) with forward slashes 372 """ 373 if type(s) == types.ListType: 374 return map(posix_slashes, s) 375 #t = re.sub(r'\\','/',s,0) # replace all 376 last = len(s)-1 377 t=[] 378 for i,a in enumerate(s): 379 x=a 380 if a == '\\': 381 if i == last: 382 x = '/' 383 elif s[i+1] != ' ': 384 x = '/' 385 t.append(x) 386 return ''.join(t)
387
388 -def glob(s):
389 """Run the normal glob.glob() on s but make sure all the slashes 390 are flipped forward afterwards. This is shorthand for 391 mbuild.posix_slashes(glob.glob(s))""" 392 import glob 393 return posix_slashes(glob.glob(s))
394
395 -def cond_add_quotes(s):
396 """If there are spaces in the input string s, put quotes around the 397 string and return it... if there are not already quotes in the 398 string. 399 400 @type s: string 401 @param s: path name 402 403 @rtype: string 404 @return: string with quotes, if necessary 405 """ 406 if re.search(r'[ ]',s) and not ( re.search(r'["].*["]',s) or 407 re.search(r"['].*[']",s) ): 408 return '\"' + s + '\"' 409 return s
410 411
412 -def escape_special_characters(s):
413 """Add a backslash before characters that have special meanings in 414 regular expressions. Python does not handle backslashes in regular 415 expressions or substitution text so they must be escaped before 416 processing.""" 417 418 special_chars = r'\\' 419 new_string = '' 420 for c in s: 421 if c in special_chars: 422 new_string += '\\' 423 new_string += c 424 return new_string
425 426 ############################################################### 427 428 if check_python_version(2,5): 429 import hashlib 430 else: 431 import sha 432
433 -def hash_list(list_of_strings):
434 """Compute a sha1 hash of a list of strings and return the hex digest""" 435 if check_python_version(2,5): 436 m = hashlib.sha1() 437 else: 438 m = sha.new() 439 map(lambda (x): m.update(x), list_of_strings) 440 d = m.hexdigest() 441 return d
442 443
444 -def hash_file(fn):
445 if os.path.exists(fn): 446 try: 447 lines = file(fn).readlines() 448 except: 449 die("COULD NOT READ: %s" % (fn)) 450 signature = hash_list(lines) 451 return signature 452 return None
453 454
455 -def write_signatures(fn,d):
456 """Write a dictionary of d[file]=hash to the specified file""" 457 # FIXME: binary protocol 2, binary file write DOES NOT WORK ON win32/win64 458 f = open(fn,"wb") 459 apickle.dump(d,f) 460 f.close()
461
462 -def read_signatures(fn):
463 """Return a dictionary of d[file]=hash from the specified file""" 464 try: 465 f = open(fn,"r") 466 d = apickle.load(f) 467 f.close() 468 return d 469 except: 470 return None
471 472
473 -def hash_string(s):
474 """Compute a sha1 hash of a string and return the hex digest""" 475 if check_python_version(2,5): 476 m = hashlib.sha1() 477 else: 478 m = sha.new() 479 m.update(s) 480 d = m.hexdigest() 481 return d
482 483
484 -def hash_files(list_of_files, fn):
485 """Hash the files in the list of files and write the hashes to fn""" 486 d = {} 487 for f in list_of_files: 488 d[f] = hash_file(f) 489 write_signatures(fn,d)
490
491 -def file_hashes_are_valid(list_of_files, fn):
492 """Return true iff the old hashes in the file fn are valid for all 493 of the specified list of files.""" 494 if not os.path.exists(fn): 495 return False 496 d = read_signatures(fn) 497 if d == None: 498 return False 499 for f in list_of_files: 500 if os.path.exists(f): 501 nhash = hash_file(f) 502 else: 503 return False 504 if nhash == None: 505 return False 506 if f not in d: 507 return False 508 elif d[f] != nhash: 509 return False; 510 return True
511 512 ############################################################### 513 # Time functions
514 -def get_time_str():
515 """@rtype: string 516 @returns: current time as string 517 """ 518 # include time zone 519 return time.strftime('%Y-%m-%d %H:%M:%S %Z')
520
521 -def get_time():
522 """@rtype: float 523 @returns: current time as float 524 """ 525 return time.time()
526
527 -def get_elapsed_time(start_time, end_time=None):
528 """compute the elapsed time in seconds or minutes 529 @type start_time: float 530 @param start_time: starting time. 531 @type end_time: float 532 @param end_time: ending time. 533 @rtype: string 534 """ 535 if end_time == None: 536 end_time = get_time() 537 seconds = end_time - start_time 538 negative_prefix = '' 539 if seconds < 0: 540 negative_prefix = '-' 541 seconds = -seconds 542 if seconds < 120: 543 if int(seconds) == 0: 544 milli_seconds = seconds * 1000 545 timestr = "%d" % int(milli_seconds) 546 suffix = " msecs" 547 else: 548 timestr = "%d" % int(seconds) 549 suffix = " secs" 550 else: 551 minutes = int(seconds/60.0) 552 remainder_seconds = int(seconds - (minutes*60)) 553 timestr = "%.d:%02d" % (minutes,remainder_seconds) 554 suffix = " min:sec" 555 return "".join([negative_prefix, timestr, suffix])
556 579 580 581 ###############################################################
582 -def _prepare_cmd(cmd):
583 """Tokenize the cmd string input. Return as list on non-windows 584 platforms. On windows, it returns the raw command string.""" 585 586 if on_native_windows(): 587 # the posix=False is required to keep shlex from eating 588 # backslashed path characters on windows. But 589 # the nonposix chokes on /Dfoo="xxx yyy" in that it'll 590 # split '/Dfoo="xxx' and 'yyy"' in to two different args. 591 # so we cannot use that 592 #args = shlex.split(cmd,posix=False) 593 594 # using posix mode (default) means that all commands must must 595 # forward slashes. So that is annoying and we avoid that 596 #args = shlex.split(cmd) 597 598 # passing the args through works fine. Make sure not to have 599 # any carriage returns or leading white space in the supplied 600 # command. 601 args = cmd 602 603 else: 604 args = shlex.split(cmd) 605 return args
606
607 -def _cond_open_input_file(directory,input_file_name):
608 if input_file_name: 609 if directory and not os.path.isabs(input_file_name): 610 fn = os.path.join(directory, input_file_name) 611 else: 612 fn = input_file_name 613 input_file_obj = file(fn,"r") 614 return input_file_obj 615 return None
616
617 -def run_command(cmd, 618 separate_stderr=False, 619 shell_executable=None, 620 directory=None, 621 osenv=None, 622 input_file_name=None, 623 **kwargs):
624 """ 625 Run a command string using the subprocess module. 626 627 @type cmd: string 628 @param cmd: command line to execut with all args. 629 @type separate_stderr: bool 630 @param separate_stderr: If True, the return tuple has a list of stderr lines as the 3rd element 631 @type shell_executable: string 632 @param shell_executable: the shell executable 633 @type directory: string 634 @param directory: a directory to change to before running the command. 635 @type osenv: dictionary 636 @param osenv: dict of environment vars to be passed to the new process 637 @type input_file_name: string 638 @param input_file_name: file name to read stdin from. Default none 639 640 @rtype: tuple 641 @return: (return code, list of stdout lines, list of lines of stderr) 642 """ 643 use_shell = False 644 if verbose(99): 645 msgb("RUN COMMAND", cmd) 646 msgb("RUN COMMAND repr", repr(cmd)) 647 stdout = None 648 stderr = None 649 cmd_args = _prepare_cmd(cmd) 650 try: 651 input_file_obj = _cond_open_input_file(directory, input_file_name) 652 653 if separate_stderr: 654 sub = subprocess.Popen(cmd_args, 655 shell=use_shell, 656 executable=shell_executable, 657 stdin = input_file_obj, 658 stdout = subprocess.PIPE, 659 stderr = subprocess.PIPE, 660 cwd=directory, 661 env=osenv, 662 **kwargs) 663 (stdout, stderr ) = sub.communicate() 664 if not isinstance(stderr,types.ListType): 665 stderr = [stderr] 666 if not isinstance(stdout,types.ListType): 667 stdout = [stdout] 668 return (sub.returncode, stdout, stderr) 669 else: 670 sub = subprocess.Popen(cmd_args, 671 shell=use_shell, 672 executable=shell_executable, 673 stdin = input_file_obj, 674 stdout = subprocess.PIPE, 675 stderr = subprocess.STDOUT, 676 cwd=directory, 677 env=osenv, 678 **kwargs) 679 stdout = sub.stdout.readlines() 680 sub.wait() 681 if not isinstance(stdout,types.ListType): 682 stdout = [stdout] 683 return (sub.returncode, stdout, None) 684 except OSError, e: 685 s= ["Execution failed for: %s\n" % (cmd) ] 686 s.append("Result is %s\n" % (str(e))) 687 # put the error message in stderr if there is a separate 688 # stderr, otherwise put it in stdout. 689 if separate_stderr: 690 if stderr == None: 691 stderr = [] 692 elif not isinstance(stderr,types.ListType): 693 stderr = [stderr] 694 if stdout == None: 695 stdout = [] 696 elif not isinstance(stdout,types.ListType): 697 stdout = [stdout] 698 if separate_stderr: 699 stderr.extend(s) 700 else: 701 stdout.extend(s) 702 return (1, stdout, stderr)
703 704
705 -def run_command_unbufferred(cmd, 706 prefix_line=None, 707 shell_executable=None, 708 directory=None, 709 osenv=None, 710 input_file_name=None, 711 **kwargs):
712 """ 713 Run a command string using the subprocess module. 714 715 @type cmd: string 716 @param cmd: command line to execut with all args. 717 @type prefix_line: string 718 @param prefix_line: a string to prefix each output line. Default None 719 @type shell_executable: string 720 @param shell_executable: NOT USED BY THIS FUNCTION 721 @type directory: string 722 @param directory: a directory to change to before running the command. 723 @type osenv: dictionary 724 @param osenv: dict of environment vars to be passed to the new process 725 @type input_file_name: string 726 @param input_file_name: file name to read stdin from. Default none 727 728 @rtype: tuple 729 @return: (return code, list of stdout lines, empty list) 730 731 """ 732 use_shell = False 733 if verbose(99): 734 msgb("RUN COMMAND", cmd) 735 msgb("RUN COMMAND repr", repr(cmd)) 736 lines = [] 737 cmd_args = _prepare_cmd(cmd) 738 try: 739 input_file_obj = _cond_open_input_file(directory, input_file_name) 740 sub = subprocess.Popen(cmd_args, 741 shell=use_shell, 742 executable=shell_executable, 743 stdin = input_file_obj, 744 stdout = subprocess.PIPE, 745 stderr = subprocess.STDOUT, 746 env=osenv, 747 cwd=directory, 748 **kwargs) 749 while 1: 750 # FIXME: 2008-12-05 bad for password prompts without newlines. 751 line = sub.stdout.readline() 752 if line == '': 753 break 754 line = line.rstrip() 755 if prefix_line: 756 msgn(prefix_line) 757 msg(line) 758 lines.append(line + "\n") 759 760 sub.wait() 761 return (sub.returncode, lines, []) 762 except OSError, e: 763 lines.append("Execution failed for: %s\n" % (cmd)) 764 lines.append("Result is %s\n" % (str(e))) 765 return (1, lines,[])
766 767
768 -def run_command_output_file(cmd, 769 output_file_name, 770 shell_executable=None, 771 directory=None, 772 osenv=None, 773 input_file_name=None, 774 **kwargs):
775 """ 776 Run a command string using the subprocess module. 777 778 @type cmd: string 779 @param cmd: command line to execut with all args. 780 @type output_file_name: string 781 @param output_file_name: output file name 782 @type shell_executable: string 783 @param shell_executable: the shell executable 784 @type directory: string 785 @param directory: a directory to change to before running the command. 786 @type osenv: dictionary 787 @param osenv: dict of environment vars to be passed to the new process 788 @type input_file_name: string 789 @param input_file_name: file name to read stdin from. Default none 790 791 @rtype: tuple 792 @return: (return code, list of stdout lines) 793 """ 794 use_shell = False 795 if verbose(99): 796 msgb("RUN COMMAND", cmd) 797 lines = [] 798 cmd_args = _prepare_cmd(cmd) 799 try: 800 output = file(output_file_name,"w") 801 input_file_obj = _cond_open_input_file(directory, input_file_name) 802 sub = subprocess.Popen(cmd_args, 803 shell=use_shell, 804 executable=shell_executable, 805 stdin = input_file_obj, 806 stdout = subprocess.PIPE, 807 stderr = subprocess.STDOUT, 808 env=osenv, 809 cwd=directory, 810 **kwargs) 811 #msgb("RUNNING SUBPROCESS") 812 while 1: 813 #msgb("READING OUTPUT") 814 line = sub.stdout.readline() 815 if line == '': 816 break 817 line = line.rstrip() 818 output.write(line + "\n") 819 lines.append(line + "\n") 820 821 output.close() 822 sub.wait() 823 return (sub.returncode, lines, []) 824 except OSError, e: 825 lines.append("Execution failed for: %s\n" % (cmd)) 826 lines.append("Result is %s\n" % (str(e))) 827 return (1, lines,[])
828
829 -def run_cmd_io(cmd, fn_i, fn_o,shell_executable=None, directory=None):
830 """ 831 Run a command string using the subprocess module. Read standard 832 input from fn_i and write stdout/stderr to fn_o. 833 834 @type cmd: string 835 @param cmd: command line to execut with all args. 836 @type fn_i: string 837 @param fn_i: input file name 838 @type fn_o: string 839 @param fn_o: output file name 840 @type shell_executable: string 841 @param shell_executable: the shell executable 842 @type directory: string 843 @param directory: a directory to change to before running the command. 844 845 @rtype: integer 846 @return: return code 847 """ 848 use_shell = False 849 cmd_args = _prepare_cmd(cmd) 850 try: 851 fin = open(fn_i,'r') 852 fout = open(fn_o,'w') 853 sub = subprocess.Popen(cmd_args, 854 shell=use_shell, 855 executable=shell_executable, 856 stdin=fin, 857 stdout=fout, 858 stderr=subprocess.STDOUT, 859 cwd=directory) 860 retval = sub.wait() 861 fin.close() 862 fout.close() 863 return retval 864 except OSError, e: 865 die("Execution failed for cmd %s\nResult is %s\n" % (cmd,str(e)))
866
867 -def find_dir(d):
868 """Look upwards for a particular filesystem directory d as a 869 subdirectory of one of the ancestors. Return None on failure""" 870 dir = os.getcwd() 871 last = '' 872 while dir != last: 873 target_dir = os.path.join(dir,d) 874 #print "Trying %s" % (target_dir) 875 if os.path.exists(target_dir): 876 return target_dir 877 last = dir 878 (dir,tail) = os.path.split(dir) 879 return None
880
881 -def peel_dir(s,n):
882 """Remove n trailing path components from s by calling 883 os.path.dirname()""" 884 t = s 885 for i in range(0,n): 886 t = os.path.dirname(t) 887 return t
888
889 -def get_gcc_version(gcc):
890 """Return the compressed version number of gcc""" 891 cmd = gcc + " -dumpversion" 892 try: 893 (retcode, stdout, stderr) = run_command(cmd) 894 if retcode == 0: 895 version = stdout[0] 896 return version.strip() 897 except: 898 return 'unknown'
899
900 -def get_clang_version(full_path):
901 cmd = full_path + " -dM -E - " 902 try: 903 (retcode, stdout, stderr) = run_command(cmd, 904 input_file_name="/dev/null") 905 if retcode == 0: 906 major=minor=patchlevel='x' 907 for line in stdout: 908 line = line.strip() 909 chunks = line.split() 910 if len(chunks) == 3: 911 if chunks[1] == '__clang_major__': 912 major = chunks[2] 913 elif chunks[1] == '__clang_minor__': 914 minor = chunks[2] 915 elif chunks[1] == '__clang_patchlevel__': 916 patchlevel = chunks[2] 917 version = "{}.{}.{}".format(major,minor,patchlevel) 918 return version 919 except: 920 return 'unknown'
921 922 # unify names for clang/gcc version checkers
923 -def compute_clang_version(full_path):
924 return get_clang_version(full_path)
925
926 -def compute_gcc_version(full_path):
927 return get_gcc_version(full_path)
928
929 -def gcc_version_test(major,minor,rev,gstr):
930 """Return True if the specified gcc version string (gstr) is at or 931 after the specified major,minor,revision args""" 932 933 n = gstr.split('.') 934 if len(n) not in [2,3]: 935 die("Cannot compute gcc version from input string: [%s]" % (gstr)) 936 ga = int(n[0]) 937 gb = int(n[1]) 938 if len(n) == 2: 939 gc = 0 940 else: 941 gc = int(n[2]) 942 943 if ga > major: 944 return True 945 if ga == major and gb > minor: 946 return True 947 if ga == major and gb == minor and gc >= rev: 948 return True 949 return False
950 951 import threading 952 # requires Python2.6 or later
953 -class _timed_command_t(threading.Thread):
954 """ 955 Internal function to mbuild util.py. Do not call directly. 956 957 Examples of use 958 env = os.environ 959 env['FOOBAR'] = 'hi' 960 # the command a.out prints out the getenv("FOOBAR") value 961 rc = _timed_command_t(["./a.out", "5"], seconds=4, env=env) 962 rc.timed_run() 963 964 rc = _timed_command_t(["/bin/sleep", "5"], seconds=4) 965 rc.timed_run() 966 """ 967
968 - def __init__(self, cmd, 969 shell_executable=None, 970 directory=None, 971 osenv=None, 972 seconds=0, 973 input_file_name=None, 974 **kwargs):
975 """The kwargs are for the other parameters to Popen""" 976 threading.Thread.__init__(self) 977 self.cmd = cmd 978 self.kwargs = kwargs 979 self.seconds = seconds 980 self.timed_out = False 981 self.sub = None 982 self.osenv= osenv 983 self.input_file_name = input_file_name 984 self.directory = directory 985 self.shell_executable = shell_executable 986 self.exception_type = None 987 self.exception_object = None 988 self.exception_trace = None 989 self.exitcode = 0, 990 self.output = "", 991 self.stderr = "",
992
993 - def run(self): # executed by calling start()
994 cmd = self.cmd 995 #run a python command 996 if _is_python_cmd(cmd): 997 kwargs = self.kwargs 998 xenv = kwargs.get('xenv') 999 args_lst = kwargs.get('args_lst') 1000 if args_lst == None: 1001 args_lst = [] 1002 if xenv == None: 1003 (self.exitcode,self.output,self.stderr) = cmd(*args_lst) 1004 else: 1005 (self.exitcode,self.output,self.stderr) = cmd(xenv, *args_lst) 1006 return 1007 1008 #run an executable 1009 use_shell = False 1010 cmd_args = _prepare_cmd(cmd) 1011 input_file_obj = _cond_open_input_file(self.directory, 1012 self.input_file_name) 1013 try: 1014 self.sub = subprocess.Popen(cmd_args, 1015 shell=use_shell, 1016 executable=self.shell_executable, 1017 cwd=self.directory, 1018 env=self.osenv, 1019 stdin = input_file_obj, 1020 **self.kwargs) 1021 except: 1022 (self.exception_type, 1023 self.exception_object, 1024 self.exception_trace) = sys.exc_info() 1025 else: 1026 self.sub.wait()
1027
1028 - def timed_run(self):
1029 """Returns False if the process times out. Also sets 1030 self.timed_out to True.""" 1031 1032 self.timed_out=False 1033 self.start() # calls run() 1034 if self.seconds: 1035 self.join(self.seconds) 1036 else: 1037 self.join() 1038 1039 if self.is_alive(): 1040 try: 1041 if self.sub: 1042 if on_windows(): 1043 # On Windows terminate() does not always kill 1044 # the process So we need specific handling for 1045 # Windows here. 1046 kill_cmd = "taskkill /F /T /PID %i" % (self.sub.pid) 1047 cmd_args = _prepare_cmd(kill_cmd) 1048 subprocess.Popen(cmd_args, shell=True) 1049 else: 1050 self.sub.kill() 1051 except: 1052 pass 1053 1054 self.join() 1055 self.timed_out=True 1056 return False 1057 return True
1058 1059
1060 -def _is_python_cmd(cmd):
1061 return isinstance(cmd,types.FunctionType)
1062 1063
1064 -def run_command_timed( cmd, 1065 shell_executable=None, 1066 directory=None, 1067 osenv=None, 1068 seconds=0, 1069 input_file_name=None, 1070 **kwargs ):
1071 """Run a timed command. kwargs are keyword args for subprocess.Popen. 1072 1073 @type cmd: string or python function 1074 @param cmd: command to run 1075 1076 @type shell_executable: string 1077 @param shell_executable: the shell executable 1078 1079 @type directory: string 1080 @param directory: the directory to run the command in 1081 1082 @type osenv: dictionary 1083 @param osenv: dict of environment vars to be passed to the new process 1084 1085 @type seconds: number 1086 @param seconds: maximum execution time in seconds 1087 1088 @type input_file_name: string 1089 @param input_file_name: input filename when redirecting stdin. 1090 1091 @type kwargs: keyword args 1092 @param kwargs: keyword args for subprocess.Popen 1093 1094 @rtype: tuple 1095 return: (return code, list of stdout+stderr lines) 1096 """ 1097 1098 def _get_exit_code(tc): 1099 exit_code = 399 1100 if tc.sub: 1101 # if tc.sub does not have a returncode, then something went 1102 # very wrong, usually an exception running the subprocess. 1103 if hasattr(tc.sub, 'returncode'): 1104 exit_code = tc.sub.returncode 1105 return exit_code
1106 1107 # we use a temporary file to hold the output because killing the 1108 # process disrupts the normal output collection mechanism. 1109 fo = tempfile.SpooledTemporaryFile() 1110 fe = tempfile.SpooledTemporaryFile() 1111 tc = _timed_command_t(cmd, 1112 shell_executable, 1113 directory, 1114 osenv, 1115 seconds, 1116 input_file_name, 1117 stdout=fo, 1118 stderr=fe, 1119 **kwargs) 1120 1121 tc.timed_run() 1122 1123 if _is_python_cmd(tc.cmd): 1124 exit_code = tc.exitcode 1125 output = tc.output 1126 stderr = tc.stderr 1127 else: 1128 fo.seek(0) 1129 output = fo.readlines() 1130 fo.close() 1131 fe.seek(0) 1132 stderr = [''.join(fe.readlines())] 1133 fe.close() 1134 exit_code = _get_exit_code(tc) 1135 1136 nl = '\n' 1137 if tc.timed_out: 1138 stderr.extend([ nl, 1139 'COMMAND TIMEOUT'+nl, 1140 'KILLING PROCCESS'+nl]) 1141 if tc.exception_type: 1142 stderr.extend([ nl, 1143 'COMMAND ENCOUNTERD AN EXCEPTION' + nl]) 1144 stderr.extend(traceback.format_exception(tc.exception_type, 1145 tc.exception_object, 1146 tc.exception_trace)) 1147 1148 return (exit_code, output, stderr) 1149