Home | History | Annotate | Download | only in google
      1 # Copyright (c) 2011 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 """Platform-specific utility methods shared by several scripts."""
      5 
      6 import os
      7 import re
      8 import subprocess
      9 import sys
     10 
     11 import google.path_utils
     12 
     13 # Cache a single cygpath process for use throughout, even across instances of
     14 # the PlatformUtility class.
     15 _cygpath_proc = None
     16 
     17 class PlatformUtility(object):
     18   def __init__(self, base_dir):
     19     """Args:
     20          base_dir: a directory above which third_party/cygwin can be found,
     21              used to locate the cygpath executable for path conversions.
     22     """
     23     self._cygwin_root = None
     24     self._base_dir = base_dir
     25 
     26   def _CygwinRoot(self):
     27     """Returns the full path to third_party/cygwin/."""
     28     if not self._cygwin_root:
     29       self._cygwin_root = google.path_utils.FindUpward(self._base_dir,
     30                                                        'third_party', 'cygwin')
     31     return self._cygwin_root
     32 
     33   def _PathToExecutable(self, executable):
     34     """Returns the full path to an executable in Cygwin's bin dir."""
     35     return os.path.join(self._CygwinRoot(), 'bin', executable)
     36 
     37   def GetAbsolutePath(self, path, force=False):
     38     """Returns an absolute windows path. If platform is cygwin, converts it to
     39     windows style using cygpath.
     40 
     41     For performance reasons, we use a single cygpath process, shared among all
     42     instances of this class. Otherwise Python can run out of file handles.
     43     """
     44     if not force and sys.platform != "cygwin":
     45       return os.path.abspath(path)
     46     global _cygpath_proc
     47     if not _cygpath_proc:
     48       cygpath_command = [self._PathToExecutable("cygpath.exe"),
     49                          "-a", "-m", "-f", "-"]
     50       _cygpath_proc = subprocess.Popen(cygpath_command,
     51                                        stdin=subprocess.PIPE,
     52                                        stdout=subprocess.PIPE)
     53     _cygpath_proc.stdin.write(path + "\n")
     54     return _cygpath_proc.stdout.readline().rstrip()
     55 
     56   def GetFilesystemRoot(self):
     57     """Returns the root directory of the file system."""
     58     return os.environ['SYSTEMDRIVE'] + '\\'
     59 
     60   def GetTempDirectory(self):
     61     """Returns the file system's base temp directory, or the filesystem root
     62     if the standard temp directory can't be determined.
     63 
     64     Note that this does not use a random subdirectory, so it's not
     65     intrinsically secure.  If you need a secure subdir, use the tempfile
     66     package.
     67     """
     68     return os.environ.get('TEMP', self.GetFilesystemRoot())
     69 
     70   def FilenameToUri(self, path, use_http=False, use_ssl=False, port=8000):
     71     """Convert a Windows style path to a URI.
     72 
     73     Args:
     74       path: For an http URI, the path relative to the httpd server's
     75           DocumentRoot; for a file URI, the full path to the file.
     76       use_http: if True, returns a URI of the form http://127.0.0.1:8000/.
     77           If False, returns a file:/// URI.
     78       use_ssl: if True, returns HTTPS URL (https://127.0.0.1:8000/).
     79           This parameter is ignored if use_http=False.
     80       port: The port number to append when returning an HTTP URI
     81     """
     82     if use_http:
     83       protocol = 'http'
     84       if use_ssl:
     85         protocol = 'https'
     86       path = path.replace("\\", "/")
     87       return "%s://127.0.0.1:%s/%s" % (protocol, str(port), path)
     88     return "file:///" + self.GetAbsolutePath(path)
     89 
     90   def GetStartHttpdCommand(self, output_dir,
     91                            httpd_conf_path, mime_types_path,
     92                            document_root=None, apache2=False):
     93     """Prepares the config file and output directory to start an httpd server.
     94     Returns a list of strings containing the server's command line+args.
     95 
     96     Args:
     97       output_dir: the path to the server's output directory, for log files.
     98           It will be created if necessary.
     99       httpd_conf_path: full path to the httpd.conf file to be used.
    100       mime_types_path: full path to the mime.types file to be used.
    101       document_root: full path to the DocumentRoot.  If None, the DocumentRoot
    102           from the httpd.conf file will be used.  Note that the httpd.conf
    103           file alongside this script does not specify any DocumentRoot, so if
    104           you're using that one, be sure to specify a document_root here.
    105       apache2: boolean if true will cause this function to return start
    106                command for Apache 2.x as opposed to Apache 1.3.x
    107     """
    108 
    109     if document_root:
    110       document_root = GetCygwinPath(document_root)
    111     exe_name = "httpd"
    112     cert_file = ""
    113     if apache2:
    114       exe_name = "httpd2"
    115       cert_file = google.path_utils.FindUpward(self._base_dir, 'tools',
    116                                                'python', 'google',
    117                                                'httpd_config', 'httpd2.pem')
    118     httpd_vars = {
    119       "httpd_executable_path": GetCygwinPath(
    120           os.path.join(self._CygwinRoot(), "usr", "sbin", exe_name)),
    121       "httpd_conf_path": GetCygwinPath(httpd_conf_path),
    122       "ssl_certificate_file": GetCygwinPath(cert_file),
    123       "document_root" : document_root,
    124       "server_root": GetCygwinPath(os.path.join(self._CygwinRoot(), "usr")),
    125       "mime_types_path": GetCygwinPath(mime_types_path),
    126       "output_dir": GetCygwinPath(output_dir),
    127       "bindir": GetCygwinPath(os.path.join(self._CygwinRoot(), "bin")),
    128       "user": os.environ.get("USERNAME", os.environ.get("USER", "")),
    129     }
    130     if not httpd_vars["user"]:
    131       # Failed to get the username from the environment; use whoami.exe
    132       # instead.
    133       proc = subprocess.Popen(self._PathToExecutable("whoami.exe"),
    134                               stdout=subprocess.PIPE)
    135       httpd_vars["user"] = proc.stdout.read().strip()
    136 
    137     if not httpd_vars["user"]:
    138       raise Exception("Failed to get username.")
    139 
    140     google.path_utils.MaybeMakeDirectory(output_dir)
    141 
    142     # We have to wrap the command in bash because the cygwin environment
    143     # is required for httpd to run.
    144     # -C: process directive before reading config files
    145     # -c: process directive after reading config files
    146     # Apache wouldn't run CGIs with permissions==700 unless we add
    147     # -c User "<username>"
    148     bash = self._PathToExecutable("bash.exe")
    149     httpd_cmd_string = (
    150       ' PATH=%(bindir)s %(httpd_executable_path)s'
    151       ' -f %(httpd_conf_path)s'
    152       ' -c \'TypesConfig "%(mime_types_path)s"\''
    153       ' -c \'CustomLog "%(output_dir)s/access_log.txt" common\''
    154       ' -c \'ErrorLog "%(output_dir)s/error_log.txt"\''
    155       ' -c \'PidFile "%(output_dir)s/httpd.pid"\''
    156       ' -C \'User "%(user)s"\''
    157       ' -C \'ServerRoot "%(server_root)s"\''
    158     )
    159     if apache2:
    160       httpd_cmd_string = ('export CYGWIN=server;' + httpd_cmd_string +
    161           ' -c \'SSLCertificateFile "%(ssl_certificate_file)s"\'')
    162     if document_root:
    163       httpd_cmd_string += ' -C \'DocumentRoot "%(document_root)s"\''
    164 
    165     httpd_cmd = [bash, "-c", httpd_cmd_string % httpd_vars]
    166     return httpd_cmd
    167 
    168   def GetStopHttpdCommand(self):
    169     """Returns a list of strings that contains the command line+args needed to
    170     stop the http server used in the http tests.
    171     """
    172     # Force kill (/f) *all* httpd processes.  This has the side effect of
    173     # killing httpd processes that we didn't start.
    174     return ["taskkill.exe", "/f", "/im", "httpd*"]
    175 
    176 ###########################################################################
    177 # This method is specific to windows, expected to be used only by *_win.py
    178 # files.
    179 
    180 def GetCygwinPath(path):
    181   """Convert a Windows path to a cygwin path.
    182 
    183   The cygpath utility insists on converting paths that it thinks are Cygwin
    184   root paths to what it thinks the correct roots are.  So paths such as
    185   "C:\b\slave\webkit-release-kjs\build\third_party\cygwin\bin" are converted to
    186   plain "/usr/bin".  To avoid this, we do the conversion manually.
    187 
    188   The path is expected to be an absolute path, on any drive.
    189   """
    190   drive_regexp = re.compile(r'([a-z]):[/\\]', re.IGNORECASE)
    191   def LowerDrive(matchobj):
    192     return '/cygdrive/%s/' % matchobj.group(1).lower()
    193   path = drive_regexp.sub(LowerDrive, path)
    194   return path.replace('\\', '/')
    195