Home | History | Annotate | Download | only in cdd
      1 #!/usr/bin/python
      2 """
      3 Utility for building the CDD from component markdown files.
      4 
      5 From the compatibility/cdd directory, run:
      6 python make-cdd.py --version <version number> --branch <AOSP branch>
      7     --output <output file name>
      8 
      9 
     10 TODO(gdimino): Clean up and comment this code.
     11 """
     12 
     13 from bs4 import BeautifulSoup
     14 import argparse
     15 import hashlib
     16 import markdown
     17 import os
     18 import pprint
     19 import re
     20 import tidylib
     21 import subprocess
     22 
     23 # TODO (gdimino): Clean up this code using templates
     24 # from jinja2 import Template
     25 
     26 HEADERS_FOR_TOC = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7']
     27 TOC_PER_COL = 34
     28 
     29 def get_section_info(my_path):
     30   # (_, _, filenames) = os.walk(my_path).next()
     31   section_info = [];
     32   # Get section info from every file whose name contains a number. TODO: fix
     33   # this ugly hack.
     34   # for rootdir, subdirs, files in os.walk(my_path):
     35   for dir in get_immediate_subdirs(my_path):
     36     # for dir  in subdirs:
     37     if (not dir.isalpha() and dir != 'older-versions' and dir != '.git'):
     38       child_data = []
     39       print 'dir = ' + dir
     40       for file in os.listdir(dir):
     41         if '.md' in file:
     42           if file == 'index.md':
     43             number =  0
     44           else:
     45             number = int((file.split('_')[1]))
     46           print 'file = ' + file + ', dir = ' + dir
     47           html_string = markdown.markdown(unicode(open(my_path + '/' + dir + '/' + file, 'r').read(), 'utf-8'))
     48           child_data.append({'file': file,
     49                              'number': number,
     50                              'title': dir.split('_')[-1],
     51                              'html': html_string,
     52                              'children':[]})
     53       child_data.sort(key=lambda child: child['number'])
     54       section_info.append({'id': dir,
     55                            'number': int(''.join((dir.split('_')[:-1])).replace("_", ".")),
     56                            'title': dir.split('_')[-1],
     57                            'html': '',
     58                            'children':child_data})
     59   section_info.sort(key=lambda section: section['number'])
     60   return section_info
     61 
     62 
     63 def get_soup(section_info):
     64   html_body_text = '''<!DOCTYPE html>
     65 <head>
     66 <title>Android ANDROID_VERSION Compatibility Definition</title>
     67 <link rel="stylesheet" type="text/css" href="source/android-cdd.css"/>
     68 </head>
     69 <body>
     70 <div id="main">'''
     71 
     72   for section in section_info:
     73      for child in section['children']:
     74        html_body_text += child['html']
     75   html_body_text +=  '</div></body><html>'
     76   return BeautifulSoup(html_body_text)
     77 
     78 
     79 def add_id_to_section_headers(soup):
     80   header_tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7']
     81   for tag in soup.find_all(header_tags):
     82     tag['id'] = create_id(tag)
     83 
     84 def generate_toc(soup):
     85   toc_html = '<div id="toc">'
     86   header_tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7']
     87   toc_entries =  soup.find_all(header_tags)
     88   toc_chunks = [toc_entries[i:i + TOC_PER_COL] for i in xrange(0, len(toc_entries), TOC_PER_COL)]
     89   print 'Number of chunks =  %d' % len(toc_chunks)
     90   for chunk in toc_chunks:
     91     if not toc_chunks.index(chunk) %2:
     92       toc_html = toc_html + ('<div id="toc_left">')
     93       for tag in chunk:
     94         toc_html = toc_html + '<p class="toc_' + tag.name + '"><a href= "#' + create_id(tag) + '">' + tag.contents[0] + '</a></p>'
     95       toc_html = toc_html + ('</div>')
     96     else:
     97       toc_html = toc_html + ('<div id="toc_right">')
     98       for tag in chunk:
     99         toc_html = toc_html + '<p class="toc_' + tag.name + '"><a href= "#' + create_id(tag) + '">' + tag.contents[0] + '</a></p>'
    100       toc_html = toc_html + ('</div>')
    101       toc_html = toc_html + '<div style="clear: both; page-break-after:always; height:1px"></div>'
    102   toc_html = toc_html + '<div style="clear: both"></div>'
    103   return (BeautifulSoup(toc_html).body.contents)
    104 
    105 def add_toc(soup):
    106   toc_contents = generate_toc(soup)[0]
    107   toc_title =  BeautifulSoup("<h6>Table of Contents</h6>").body.contents[0]
    108   soup.body.insert(0, toc_contents)
    109   soup.body.insert(0, toc_title)
    110   return soup
    111 
    112 def create_id(header_tag):
    113   return header_tag.contents[0].lower().replace('. ', '_').replace(' ', '_').replace('.', '_')
    114 
    115 # Utilities
    116 def get_immediate_subdirs(dir):
    117     return [name for name in os.listdir(dir)
    118             if os.path.isdir(os.path.join(dir, name))]
    119 
    120 # Odds and ends
    121 
    122 def check_section_numbering(soup):
    123   header_tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7']
    124   for tag in header_tags:
    125     headings = soup.find_all(tag)
    126     header_numbers = []
    127     for heading in headings:
    128       header_numbers.append(re.sub(r"([\d.]*).*", r"\1"), heading.contents)
    129   return true
    130 
    131 def get_version_branch_and_output():
    132 
    133   # Get command-line args.  If there aren't any, then prompt for user input.
    134   parser = argparse.ArgumentParser()
    135   parser.add_argument('--version', help='Android version')
    136   parser.add_argument('--branch', help='AOSP branch')
    137   parser.add_argument('--output', help='Base name of output file')
    138   args = parser.parse_args()
    139 
    140   if not args.version:
    141     args.version = raw_input('Android version for CDD: ')
    142   if not args.branch:
    143     args.branch = raw_input('Current AOSP branch for changelog: ')
    144   if not args.output:
    145     args.output = raw_input('Base name of desired output file: ')
    146 
    147   return (args.version, args.branch, args.output)
    148 
    149 def remove_space_before_punctuation(input):
    150   space_before_punc = r'\s+([.,:])'
    151   return re.sub(space_before_punc, '\1')
    152 
    153 def main():
    154   # Read version and branch info and output file name.
    155   (ANDROID_VERSION, CURRENT_BRANCH, output_filename) = get_version_branch_and_output()
    156 
    157   # Scan current directory for source files and compile info for the toc..
    158   my_path = os.getcwd()
    159   section_info = get_section_info(my_path)
    160 
    161   # Generate the HTML
    162   soup = get_soup(section_info)
    163   add_id_to_section_headers(soup)
    164   add_toc(soup)
    165   html = soup.prettify(formatter='html')
    166 
    167   # Add version and branch info
    168   html = re.sub(re.compile(r"ANDROID_VERSION"), ANDROID_VERSION, html)
    169   html = re.sub(re.compile(r"CURRENT_BRANCH"), CURRENT_BRANCH, html)
    170 
    171   # Apply HTML Tidy to output
    172   (document, errors) = tidylib.tidy_document(html, options={})
    173 
    174   # Write output file
    175   output = open('%s.html' % output_filename, "w")
    176   output.write(document.encode('utf-8'))
    177   output.close()
    178 
    179 
    180 if __name__ == '__main__':
    181   main()
    182 
    183 
    184 
    185 
    186