Home | History | Annotate | Download | only in build
      1 # Copyright 2013 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 import logging
      5 import optparse
      6 import os
      7 import pkgutil
      8 import pydoc
      9 import re
     10 import sys
     11 
     12 import telemetry
     13 from telemetry.core import util
     14 
     15 telemetry_dir = util.GetTelemetryDir()
     16 docs_dir = os.path.join(telemetry_dir, 'docs')
     17 
     18 def RemoveAllDocs():
     19   for dirname, _, filenames in os.walk(docs_dir):
     20     for filename in filenames:
     21       os.remove(os.path.join(dirname, filename))
     22 
     23 def GenerateHTMLForModule(module):
     24   html = pydoc.html.page(pydoc.describe(module),
     25                          pydoc.html.document(module, module.__name__))
     26 
     27   # pydoc writes out html with links in a variety of funky ways. We need
     28   # to fix them up.
     29   assert not telemetry_dir.endswith(os.sep)
     30   links = re.findall('(<a href="(.+?)">(.+?)</a>)', html)
     31   for link_match in links:
     32     link, href, link_text = link_match
     33     if not href.startswith('file:'):
     34       continue
     35 
     36     new_href = href.replace('file:', '')
     37     new_href = new_href.replace(telemetry_dir, os.pardir)
     38     new_href = new_href.replace(os.sep, '/')
     39 
     40     new_link_text = link_text.replace(telemetry_dir + os.sep, '')
     41 
     42     new_link = '<a href="%s">%s</a>' % (new_href, new_link_text)
     43     html = html.replace(link, new_link)
     44 
     45   # pydoc writes out html with absolute path file links. This is not suitable
     46   # for checked in documentation. So, fix up the HTML after it is generated.
     47   #html = re.sub('href="file:%s' % telemetry_dir, 'href="..', html)
     48   #html = re.sub(telemetry_dir + os.sep, '', html)
     49   return html
     50 
     51 def WriteHTMLForModule(module):
     52   page = GenerateHTMLForModule(module)
     53   path = os.path.join(docs_dir, '%s.html' % module.__name__)
     54   with open(path, 'w') as f:
     55     sys.stderr.write('Wrote %s\n' % os.path.relpath(path))
     56     f.write(page)
     57 
     58 def GetAllModulesToDocument(module):
     59   modules = [module]
     60   for _, modname, _ in pkgutil.walk_packages(
     61       module.__path__, module.__name__ + '.'):
     62     if modname.endswith('_unittest'):
     63       logging.debug("skipping %s due to being a unittest", modname)
     64       continue
     65 
     66     module = __import__(modname, fromlist=[""])
     67     name, _ = os.path.splitext(module.__file__)
     68     if not os.path.exists(name + '.py'):
     69       logging.info("skipping %s due to being an orphan .pyc", module.__file__)
     70       continue
     71 
     72     modules.append(module)
     73   return modules
     74 
     75 class AlreadyDocumentedModule(object):
     76   def __init__(self, filename):
     77     self.filename = filename
     78 
     79   @property
     80   def name(self):
     81     basename = os.path.basename(self.filename)
     82     return os.path.splitext(basename)[0]
     83 
     84   @property
     85   def contents(self):
     86     with open(self.filename, 'r') as f:
     87       return f.read()
     88 
     89 def GetAlreadyDocumentedModules():
     90   modules = []
     91   for dirname, _, filenames in os.walk(docs_dir):
     92     for filename in filenames:
     93       path = os.path.join(dirname, filename)
     94       modules.append(AlreadyDocumentedModule(path))
     95   return modules
     96 
     97 
     98 def IsUpdateDocsNeeded():
     99   already_documented_modules = GetAlreadyDocumentedModules()
    100   already_documented_modules_by_name = dict(
    101     (module.name, module) for module in already_documented_modules)
    102   current_modules = GetAllModulesToDocument(telemetry)
    103 
    104   # Quick check: if the names of modules has changed, we definitely need
    105   # an update.
    106   already_documented_module_names = set(
    107     m.name for m in already_documented_modules)
    108 
    109   current_module_names = set([m.__name__ for m in current_modules])
    110 
    111   if current_module_names != already_documented_module_names:
    112     return True
    113 
    114   # Generate the new docs and compare aganist the old. If changed, then a
    115   # an update is needed.
    116   for current_module in current_modules:
    117     already_documented_module = already_documented_modules_by_name[
    118       current_module.__name__]
    119     current_html = GenerateHTMLForModule(current_module)
    120     if current_html != already_documented_module.contents:
    121       return True
    122 
    123   return False
    124 
    125 def Main(args):
    126   parser = optparse.OptionParser()
    127   parser.add_option(
    128       '-v', '--verbose', action='count', dest='verbosity',
    129       help='Increase verbosity level (repeat as needed)')
    130   options, args = parser.parse_args(args)
    131   if options.verbosity >= 2:
    132     logging.getLogger().setLevel(logging.DEBUG)
    133   elif options.verbosity:
    134     logging.getLogger().setLevel(logging.INFO)
    135   else:
    136     logging.getLogger().setLevel(logging.WARNING)
    137 
    138   assert os.path.isdir(docs_dir)
    139 
    140   RemoveAllDocs()
    141 
    142   old_cwd = os.getcwd()
    143   try:
    144     os.chdir(telemetry_dir)
    145     for module in GetAllModulesToDocument(telemetry):
    146       WriteHTMLForModule(module)
    147   finally:
    148     os.chdir(old_cwd)
    149