1 # Copyright 2013 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import fnmatch 6 import json 7 import os 8 import pipes 9 import shlex 10 import shutil 11 import subprocess 12 import sys 13 import traceback 14 15 16 def MakeDirectory(dir_path): 17 try: 18 os.makedirs(dir_path) 19 except OSError: 20 pass 21 22 23 def DeleteDirectory(dir_path): 24 if os.path.exists(dir_path): 25 shutil.rmtree(dir_path) 26 27 28 def Touch(path): 29 MakeDirectory(os.path.dirname(path)) 30 with open(path, 'a'): 31 os.utime(path, None) 32 33 34 def FindInDirectory(directory, filter): 35 files = [] 36 for root, dirnames, filenames in os.walk(directory): 37 matched_files = fnmatch.filter(filenames, filter) 38 files.extend((os.path.join(root, f) for f in matched_files)) 39 return files 40 41 42 def FindInDirectories(directories, filter): 43 all_files = [] 44 for directory in directories: 45 all_files.extend(FindInDirectory(directory, filter)) 46 return all_files 47 48 49 def ParseGypList(gyp_string): 50 # The ninja generator doesn't support $ in strings, so use ## to 51 # represent $. 52 # TODO(cjhopman): Remove when 53 # https://code.google.com/p/gyp/issues/detail?id=327 54 # is addressed. 55 gyp_string = gyp_string.replace('##', '$') 56 return shlex.split(gyp_string) 57 58 59 def CheckOptions(options, parser, required=[]): 60 for option_name in required: 61 if not getattr(options, option_name): 62 parser.error('--%s is required' % option_name.replace('_', '-')) 63 64 def WriteJson(obj, path, only_if_changed=False): 65 old_dump = None 66 if os.path.exists(path): 67 with open(path, 'r') as oldfile: 68 old_dump = oldfile.read() 69 70 new_dump = json.dumps(obj) 71 72 if not only_if_changed or old_dump != new_dump: 73 with open(path, 'w') as outfile: 74 outfile.write(new_dump) 75 76 def ReadJson(path): 77 with open(path, 'r') as jsonfile: 78 return json.load(jsonfile) 79 80 81 # This can be used in most cases like subprocess.check_call. The output, 82 # particularly when the command fails, better highlights the command's failure. 83 # This call will directly exit on a failure in the subprocess so that no python 84 # stacktrace is printed after the output of the failed command (and will 85 # instead print a python stack trace before the output of the failed command) 86 def CheckCallDie(args, suppress_output=False, cwd=None): 87 if not cwd: 88 cwd = os.getcwd() 89 90 child = subprocess.Popen(args, 91 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd) 92 93 stdout, _ = child.communicate() 94 95 if child.returncode: 96 stacktrace = traceback.extract_stack() 97 print >> sys.stderr, ''.join(traceback.format_list(stacktrace)) 98 # A user should be able to simply copy and paste the command that failed 99 # into their shell. 100 copyable_command = ' '.join(map(pipes.quote, args)) 101 copyable_command = ('( cd ' + os.path.abspath(cwd) + '; ' 102 + copyable_command + ' )') 103 print >> sys.stderr, 'Command failed:', copyable_command, '\n' 104 105 if stdout: 106 print stdout, 107 108 # Directly exit to avoid printing stacktrace. 109 sys.exit(child.returncode) 110 111 else: 112 if stdout and not suppress_output: 113 print stdout, 114 return stdout 115 116 117 def GetModifiedTime(path): 118 # For a symlink, the modified time should be the greater of the link's 119 # modified time and the modified time of the target. 120 return max(os.lstat(path).st_mtime, os.stat(path).st_mtime) 121 122 123 def IsTimeStale(output, inputs): 124 if not os.path.exists(output): 125 return True 126 127 output_time = GetModifiedTime(output) 128 for input in inputs: 129 if GetModifiedTime(input) > output_time: 130 return True 131 return False 132 133 134 def IsDeviceReady(): 135 device_state = CheckCallDie(['adb', 'get-state'], suppress_output=True) 136 return device_state.strip() == 'device' 137 138 139 def PrintWarning(message): 140 print 'WARNING: ' + message 141 142 143 def PrintBigWarning(message): 144 print '***** ' * 8 145 PrintWarning(message) 146 print '***** ' * 8 147