Home | History | Annotate | Download | only in telemetry_tools
      1 # Copyright (c) 2012 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 """Bootstrap Chrome Telemetry by downloading all its files from SVN servers.
      6 
      7 Requires a DEPS file to specify which directories on which SVN servers
      8 are required to run Telemetry. Format of that DEPS file is a subset of the
      9 normal DEPS file format[1]; currently only only the "deps" dictionary is
     10 supported and nothing else.
     11 
     12 Fetches all files in the specified directories using WebDAV (SVN is WebDAV under
     13 the hood).
     14 
     15 [1] http://dev.chromium.org/developers/how-tos/depottools#TOC-DEPS-file
     16 """
     17 
     18 import imp
     19 import logging
     20 import os
     21 import urllib
     22 import urlparse
     23 
     24 # Link to file containing the 'davclient' WebDAV client library.
     25 _DAVCLIENT_URL = ('https://src.chromium.org/viewvc/chrome/trunk/src/tools/' +
     26                   'telemetry/third_party/davclient/davclient.py')
     27 
     28 # Dummy module for Davclient.
     29 _davclient = None
     30 
     31 def _download_and_import_davclient_module():
     32   """Dynamically import davclient helper library."""
     33   global _davclient
     34   davclient_src = urllib.urlopen(_DAVCLIENT_URL).read()
     35   _davclient = imp.new_module('davclient')
     36   exec davclient_src in _davclient.__dict__
     37 
     38 
     39 class DAVClientWrapper():
     40   """Knows how to retrieve subdirectories and files from WebDAV/SVN servers."""
     41 
     42   def __init__(self, root_url):
     43     """Initialize SVN server root_url, save files to local dest_dir.
     44 
     45     Args:
     46       root_url: string url of SVN/WebDAV server
     47     """
     48     self.root_url = root_url
     49     self.client = _davclient.DAVClient(root_url)
     50 
     51   @staticmethod
     52   def __norm_path_keys(dict_with_path_keys):
     53     """Returns a dictionary with os.path.normpath called on every key."""
     54     return dict((os.path.normpath(k), v) for (k, v) in
     55                 dict_with_path_keys.items())
     56 
     57   def GetDirList(self, path):
     58     """Returns string names of all files and subdirs of path on the server."""
     59     props = self.__norm_path_keys(self.client.propfind(path, depth=1))
     60     # remove this path
     61     del props[os.path.normpath(path)]
     62     return [os.path.basename(p) for p in props.keys()]
     63 
     64   def IsFile(self, path):
     65     """Returns True if the path is a file on the server, False if directory."""
     66     props = self.__norm_path_keys(self.client.propfind(path, depth=1))
     67     return props[os.path.normpath(path)]['resourcetype'] is None
     68 
     69   def Traverse(self, src_path, dst_path):
     70     """Walks the directory hierarchy pointed to by src_path download all files.
     71 
     72     Recursively walks src_path and saves all files and subfolders into
     73     dst_path.
     74 
     75     Args:
     76       src_path: string path on SVN server to save (absolute path on server).
     77       dest_path: string local path (relative or absolute) to save to.
     78     """
     79     if self.IsFile(src_path):
     80       if not os.path.exists(os.path.dirname(dst_path)):
     81         logging.info("creating %s", os.path.dirname(dst_path))
     82         os.makedirs(os.path.dirname(dst_path))
     83       logging.info("Saving %s to %s", self.root_url + src_path, dst_path)
     84       urllib.urlretrieve(self.root_url + src_path, dst_path)
     85       return
     86     else:
     87       for subdir in self.GetDirList(src_path):
     88         self.Traverse(os.path.join(src_path, subdir),
     89                       os.path.join(dst_path, subdir))
     90 
     91 
     92 def ListAllDepsPaths(deps_content):
     93   """Recursively returns a list of all paths indicated in this deps file.
     94 
     95   Note that this discards information about where path dependencies come from,
     96   so this is only useful in the context of a Chromium source checkout that has
     97   already fetched all dependencies.
     98 
     99   Args:
    100     deps_content: String containing deps information to be evaluated, in the
    101                   format given in the header of this file.
    102   Returns: A list of string paths starting under src that are required by the
    103            given deps file, and all of its sub-dependencies. This amounts to
    104            the keys of the 'deps' dictionary.
    105   """
    106   chrome_root = os.path.dirname(__file__)
    107   while os.path.basename(chrome_root) != 'src':
    108     chrome_root = os.path.abspath(os.path.join(chrome_root, '..'))
    109   deps = imp.new_module('deps')
    110   exec deps_content in deps.__dict__
    111 
    112   deps_paths = deps.deps.keys()
    113 
    114   if hasattr(deps, 'deps_includes'):
    115     for path in deps.deps_includes.keys():
    116       # Need to localize the paths.
    117       path = os.path.join(chrome_root, '..', path)
    118       deps_paths = deps_paths + ListAllDepsPaths(open(path).read())
    119 
    120   return deps_paths
    121 
    122 
    123 def DownloadDepsURL(destination_dir, url):
    124   """Wrapper around DownloadDeps that takes a string URL to the deps file.
    125 
    126   Args:
    127     destination_dir: String path to local directory to download files into.
    128     url: URL of deps file (see DownloadDeps for format).
    129   """
    130   logging.warning('Downloading deps from %s...', url)
    131   DownloadDeps(destination_dir, urllib.urlopen(url).read())
    132 
    133 
    134 def DownloadDeps(destination_dir, deps_content):
    135   """Saves all the dependencies in deps_path.
    136 
    137   Reads deps_content, assuming the contents are in the simple DEPS-like file
    138   format specified in the header of this file, then download all
    139   files/directories listed to the destination_dir.
    140 
    141   Args:
    142     destination_dir: String path to directory to download files into.
    143     deps_content: String containing deps information to be evaluated.
    144   """
    145   # TODO(wiltzius): Add a parameter for which revision to pull.
    146   _download_and_import_davclient_module()
    147 
    148   deps = imp.new_module('deps')
    149   exec deps_content in deps.__dict__
    150 
    151   for dst_path, src_path in deps.deps.iteritems():
    152     full_dst_path = os.path.join(destination_dir, dst_path)
    153     parsed_url = urlparse.urlparse(src_path)
    154     root_url = parsed_url.scheme + '://' + parsed_url.netloc
    155 
    156     dav_client = DAVClientWrapper(root_url)
    157     dav_client.Traverse(parsed_url.path, full_dst_path)
    158 
    159   if hasattr(deps, 'deps_includes'):
    160     for url in deps.deps_includes.values():
    161       DownloadDepsURL(destination_dir, url)
    162 
    163