Home | History | Annotate | Download | only in util
      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