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