1868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Generic utilities for all python scripts."""
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import atexit
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import httplib
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import signal
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import stat
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import subprocess
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import tempfile
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import urlparse
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def GetPlatformName():
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Return a string to be used in paths for the platform."""
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if IsWindows():
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 'win'
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if IsMac():
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 'mac'
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if IsLinux():
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 'linux'
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  raise NotImplementedError('Unknown platform "%s".' % sys.platform)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def IsWindows():
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return sys.platform == 'cygwin' or sys.platform.startswith('win')
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def IsLinux():
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return sys.platform.startswith('linux')
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def IsMac():
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return sys.platform.startswith('darwin')
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def _DeleteDir(path):
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Deletes a directory recursively, which must exist."""
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Don't use shutil.rmtree because it can't delete read-only files on Win.
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for root, dirs, files in os.walk(path, topdown=False):
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for name in files:
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      filename = os.path.join(root, name)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      os.chmod(filename, stat.S_IWRITE)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      os.remove(filename)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for name in dirs:
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      os.rmdir(os.path.join(root, name))
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  os.rmdir(path)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Delete(path):
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Deletes the given file or directory (recursively), which must exist."""
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if os.path.isdir(path):
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    _DeleteDir(path)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    os.remove(path)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def MaybeDelete(path):
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Deletes the given file or directory (recurisvely), if it exists."""
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if os.path.exists(path):
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Delete(path)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def MakeTempDir(parent_dir=None):
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Creates a temporary directory and returns an absolute path to it.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  The temporary directory is automatically deleted when the python interpreter
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exits normally.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parent_dir: the directory to create the temp dir in. If None, the system
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                temp dir is used.
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The absolute path to the temporary directory.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  path = tempfile.mkdtemp(dir=parent_dir)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  atexit.register(MaybeDelete, path)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return path
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Unzip(zip_path, output_dir):
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Unzips the given zip file using a system installed unzip tool.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    zip_path: zip file to unzip.
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output_dir: directory to unzip the contents of the zip file. The directory
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                must exist.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Raises:
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RuntimeError if the unzip operation fails.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if IsWindows():
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    unzip_cmd = ['C:\\Program Files\\7-Zip\\7z.exe', 'x', '-y']
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    unzip_cmd = ['unzip', '-o']
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unzip_cmd += [zip_path]
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if RunCommand(unzip_cmd, output_dir) != 0:
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise RuntimeError('Unable to unzip %s to %s' % (zip_path, output_dir))
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Kill(pid):
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Terminate the given pid."""
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if IsWindows():
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    subprocess.call(['taskkill.exe', '/T', '/F', '/PID', str(pid)])
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    os.kill(pid, signal.SIGTERM)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def RunCommand(cmd, cwd=None):
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Runs the given command and returns the exit code.
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cmd: list of command arguments.
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cwd: working directory to execute the command, or None if the current
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         working directory should be used.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The exit code of the command.
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  process = subprocess.Popen(cmd, cwd=cwd)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  process.wait()
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return process.returncode
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def DoesUrlExist(url):
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Determines whether a resource exists at the given URL.
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Args:
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    url: URL to be verified.
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Returns:
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    True if url exists, otherwise False.
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  parsed = urlparse.urlparse(url)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  try:
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    conn = httplib.HTTPConnection(parsed.netloc)
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    conn.request('HEAD', parsed.path)
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    response = conn.getresponse()
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  except (socket.gaierror, socket.error):
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return False
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  finally:
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    conn.close()
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Follow both permanent (301) and temporary (302) redirects.
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if response.status == 302 or response.status == 301:
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return DoesUrlExist(response.getheader('location'))
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return response.status == 200
152