ambuild2/util.py
author David Anderson <dvander@alliedmods.net>
Sat Feb 15 12:24:48 2014 -0800 (2014-02-15)
changeset 242 10388f39ac2b
parent 241 7d323620251f
child 243 eaa3bec37766
permissions -rw-r--r--
Take two at console decoding.
     1 # vim: set sts=2 ts=8 sw=2 tw=99 et: 
     2 import errno
     3 import subprocess
     4 import re, os, sys, locale
     5 import multiprocessing
     6 try:
     7   import __builtin__ as builtins
     8 except:
     9   import builtins
    10 try:
    11   import cPickle as pickle
    12 except:
    13   import pickle
    14 
    15 def Platform():
    16   if IsWindows():
    17     return 'windows'
    18   if IsMac():
    19     return 'mac'
    20   if IsLinux():
    21     return 'linux'
    22   if IsFreeBSD():
    23     return 'freebsd'
    24   if IsOpenBSD():
    25     return 'openbsd'
    26   if IsNetBSD():
    27     return 'netbsd'
    28   if IsSolaris():
    29     return 'solaris'
    30   if IsCygwin():
    31     return 'cygwin'
    32   return 'unknown'
    33 
    34 def IsLinux():
    35   return sys.platform[0:5] == 'linux'
    36 
    37 def IsFreeBSD():
    38   return sys.platform[0:7] == 'freebsd'
    39 
    40 def IsNetBSD():
    41   return sys.platform[0:6] == 'netbsd'
    42 
    43 def IsOpenBSD():
    44   return sys.platform[0:7] == 'openbsd'
    45 
    46 def IsWindows():
    47   return sys.platform == 'win32'
    48 
    49 def IsCygwin():
    50   return sys.platform == 'cygwin'
    51 
    52 def IsMac():
    53   return sys.platform == 'darwin' or sys.platform[0:6] == 'Darwin'
    54 
    55 def IsSolaris():
    56   return sys.platform[0:5] == 'sunos'
    57 
    58 def IsUnixy():
    59   return not IsWindows()
    60 
    61 def IsBSD():
    62   return IsMac() or IsFreeBSD() or IsOpenBSD() or IsNetBSD()
    63 
    64 def ExecutableSuffix():
    65   if IsWindows():
    66     return '.exe'
    67   else:
    68     return ''
    69 
    70 def SharedLibSuffix():
    71   if IsWindows():
    72     return '.dll'
    73   elif sys.platform == 'darwin':
    74     return '.dylib'
    75   else:
    76     return '.so'
    77 
    78 def StaticLibSuffix():
    79   if IsUnixy():
    80     return '.a'
    81   return '.lib'
    82 
    83 def StaticLibPrefix():
    84   if IsWindows():
    85     return ''
    86   else:
    87     return 'lib'
    88 
    89 def WaitForProcess(process):
    90   out, err = process.communicate()
    91   process.stdoutText = DecodeConsoleText(sys.stdout, process.stdout, out)
    92   process.stderrText = DecodeConsoleText(sys.stderr, process.stderr, err)
    93   return process.returncode
    94 
    95 def CreateProcess(argv, executable = None):
    96   pargs = { 'args': argv }
    97   pargs['stdout'] = subprocess.PIPE
    98   pargs['stderr'] = subprocess.PIPE
    99   if executable != None:
   100     pargs['executable'] = executable
   101   try:
   102     process = subprocess.Popen(**pargs)
   103   except:
   104     return None
   105   return process
   106 
   107 def MakePath(*list):
   108   path = os.path.join(*list)
   109   if IsWindows():
   110     path = path.replace('\\\\', '\\')
   111   return path
   112 
   113 def RemoveFolderAndContents(path):
   114   for file in os.listdir(path):
   115     subpath = os.path.join(path, file)
   116     try:
   117       if os.path.isfile(subpath) or os.path.islink(subpath):
   118         os.unlink(subpath)
   119       elif os.path.isdir(subpath):
   120         RemoveFolderAndContents(subpath)
   121     except:
   122       pass
   123   os.rmdir(path)
   124 
   125 class FolderChanger:
   126   def __init__(self, folder):
   127     self.old = os.getcwd()
   128     self.new = folder
   129 
   130   def __enter__(self):
   131     if self.new:
   132       os.chdir(self.new)
   133 
   134   def __exit__(self, type, value, traceback):
   135     os.chdir(self.old)
   136 
   137 class Guard:
   138   def __init__(self, obj):
   139     self.obj = obj
   140 
   141   def __enter__(self):
   142     return self.obj
   143 
   144   def __exit__(self, type, value, traceback):
   145     self.obj.close()
   146 
   147 def Execute(argv, shell=False):
   148   p = subprocess.Popen(
   149       args=argv,
   150       stdout=subprocess.PIPE,
   151       stderr=subprocess.PIPE,
   152       shell=shell
   153   )
   154   stdout, stderr = p.communicate()
   155   out = stdout.decode('utf8')
   156   err = stderr.decode('utf8')
   157   return p, out, err
   158 
   159 def typeof(x):
   160   return builtins.type(x)
   161 
   162 if str == bytes:
   163   # Python 2.7. sqlite blob is buffer
   164   BlobType = buffer
   165 else:
   166   BlobType = bytes
   167 
   168 def Unpickle(blob):
   169   if type(blob) != bytes:
   170     blob = bytes(blob)
   171   return pickle.loads(blob)
   172 
   173 # We don't want to dump Python 3 pickle format since then ambuild couldn't
   174 # run it if you using Python 2.
   175 PICKLE_PROTOCOL = min(2, pickle.HIGHEST_PROTOCOL)
   176 
   177 def DiskPickle(obj, fp):
   178   return pickle.dump(obj, fp, PICKLE_PROTOCOL)
   179 
   180 def CompatPickle(obj):
   181   return pickle.dumps(obj, PICKLE_PROTOCOL)
   182 
   183 def str2b(s):
   184   if bytes is str:
   185     return s
   186   return bytes(s, 'utf8')
   187 
   188 sReadIncludes = 0
   189 sLookForIncludeGuard = 1
   190 sFoundIncludeGuard = 2
   191 sIgnoring = 3
   192 def ParseGCCDeps(text):
   193   deps = set()
   194   strip = False
   195   new_text = ''
   196 
   197   state = sReadIncludes
   198   for line in re.split('\n+', text):
   199     if state == sReadIncludes:
   200       m = re.match('\.+\s+(.+)\s*$', line)
   201       if m == None:
   202         state = sLookForIncludeGuard
   203       else:
   204         name = m.groups()[0]
   205         if os.path.exists(name):
   206           strip = True
   207           deps.add(name)
   208         else:
   209           state = LookForIncludeGuard
   210     if state == sLookForIncludeGuard:
   211       if line.startswith('Multiple include guards may be useful for:'):
   212         state = sFoundIncludeGuard
   213         strip = True
   214       else:
   215         state = sReadIncludes
   216         strip = False
   217     elif state == sFoundIncludeGuard:
   218       if not line in deps:
   219         strip = False
   220         state = sIgnoring
   221     if not strip and len(line):
   222       new_text += line + '\n'
   223   return new_text, deps
   224 
   225 def ParseMSVCDeps(out):
   226   deps = []
   227   new_text = ''
   228   out = out.replace('\r\n', '\n')
   229   out = out.replace('\r', '\n')
   230   for line in out.split('\n'):
   231     m = re.match('Note: including file:\s+(.+)$', line)
   232     if m != None:
   233       file = m.groups()[0].strip()
   234       deps.append(file)
   235     else:
   236       new_text += line + '\n'
   237   return new_text, deps
   238 
   239 def ParseSunDeps(text):
   240   deps = set()
   241   new_text = ''
   242 
   243   for line in re.split('\n+', text):
   244     name = line.lstrip()
   245     if os.path.isfile(name):
   246       deps.add(name)
   247     else:
   248       new_text += line + '\n'
   249   return new_text.strip(), deps
   250 
   251 if hasattr(os, 'symlink'):
   252   def symlink(target, link):
   253     os.symlink(target, link)
   254     return 0, '', ''
   255 elif IsWindows():
   256   def symlink(target, link):
   257     argv = [
   258       'mklink',
   259       '"{0}"'.format(link),
   260       '"{0}"'.format(target)
   261     ]
   262     p, out, err = Execute(argv, shell=True)
   263     return p.returncode, out, err
   264 
   265 if IsUnixy():
   266   ConsoleGreen = lambda fp: fp.write('\033[92m')
   267   ConsoleRed = lambda fp: fp.write('\033[91m')
   268   ConsoleNormal = lambda fp: fp.write('\033[0m')
   269   ConsoleBlue = lambda fp: fp.write('\033[94m')
   270   ConsoleHeader = lambda fp: fp.write('\033[95m')
   271 elif IsWindows():
   272   def SwitchColor(fp, color):
   273     from ambuild2.ipc import winapi
   274 
   275     std = None
   276     if fp == sys.stdout:
   277       std = winapi.STD_OUTPUT_HANDLE
   278     elif fp == sys.stdin:
   279       std = winapi.STD_ERROR_HANDLE
   280     if std is None:
   281       return
   282 
   283     handle = winapi.GetStdHandle(std)
   284     winapi.SetConsoleTextAttribute(handle, color)
   285 
   286   ConsoleGreen = lambda fp: SwitchColor(fp, 0xA)
   287   ConsoleRed = lambda fp: SwitchColor(fp, 0xC)
   288   ConsoleNormal = lambda fp: SwitchColor(fp, 0x7)
   289   ConsoleBlue = lambda fp: SwitchColor(fp, 0x9)
   290   ConsoleHeader = lambda fp: SwitchColor(fp, 0xD)
   291 else:
   292   ConsoleGreen = ''
   293   ConsoleRed = ''
   294   ConsoleNormal = ''
   295   ConsoleBlue = ''
   296   ConsoleHeader = ''
   297 
   298 def IsColor(text):
   299   return IsLambda(text)
   300 
   301 sConsoleColorsEnabled = True
   302 def DisableConsoleColors():
   303   global sConsoleColorsEnabled
   304   sConsoleColorsEnabled = False
   305 
   306 def con_print(fp, args):
   307   for arg in args:
   308     if IsColor(arg):
   309       arg(fp)
   310     else:
   311       fp.write(arg)
   312   fp.write('\n')
   313 
   314 def con_print_simple(fp, args):
   315   for arg in args:
   316     if IsColor(arg):
   317       continue
   318     fp.write(arg)
   319   fp.write('\n')
   320 
   321 def con_out(*args):
   322   if sys.stdout.isatty():
   323     con_print(sys.stdout, args)
   324   else:
   325     con_print_simple(sys.stdout, args)
   326 
   327 def con_err(*args):
   328   if sys.stderr.isatty():
   329     con_print(sys.stderr, args)
   330   else:
   331     con_print_simple(sys.stderr, args)
   332 
   333 LambdaType = type(lambda: None)
   334 
   335 def IsLambda(v):
   336   return type(v) == LambdaType
   337 
   338 if str == bytes:
   339   def IsString(v):
   340     return type(v) == str or type(v) == unicode
   341 else:
   342   def IsString(v):
   343     return type(v) == str
   344 
   345 class Expando(object):
   346   pass
   347 
   348 def rm_path(path):
   349   assert not os.path.isabs(path)
   350 
   351   if os.path.exists(path):
   352     con_out(ConsoleHeader, 'Removing old output: ',
   353             ConsoleBlue, '{0}'.format(path),
   354             ConsoleNormal)
   355 
   356   try:
   357     os.unlink(path)
   358   except OSError as exn:
   359     if exn.errno != errno.ENOENT:
   360       con_err(ConsoleRed, 'Could not remove file: ',
   361               ConsoleBlue, '{0}'.format(path),
   362               ConsoleNormal, '\n',
   363               ConsoleRed, '{0}'.format(exn),
   364               ConsoleNormal)
   365       raise
   366 
   367 def try_decode_with_pipe(pipe, text):
   368   try:
   369     if hasattr(pipe, 'encoding') and pipe.encoding:
   370       return text.decode(pipe.encoding, 'replace')
   371   except:
   372     return None
   373 
   374 def DecodeConsoleText(origin, pipe, text):
   375   result = try_decode_with_pipe(pipe, text)
   376   if result is None:
   377     result = try_decode_with_pipe(origin, text)
   378     if result is None:
   379       try:
   380         result = text.decode(locale.getpreferredencoding(), 'replace')
   381       except:
   382         result = text
   383   return result.encode('utf8')
   384 
   385 def WriteEncodedText(fd, text):
   386   if not hasattr(fd, 'encoding') or fd.encoding == None:
   387     text = text.encode(locale.getpreferredencoding(), 'replace')
   388   fd.write(text)