1#!/usr/bin/python 2""" 3Utility for building the CDD from component markdown files. 4 5From the compatibility/cdd directory, run: 6python make-cdd.py --version <version number> --branch <AOSP branch> 7 --output <output file name> 8 9 10TODO(gdimino): Clean up and comment this code. 11""" 12 13from bs4 import BeautifulSoup 14import argparse 15import hashlib 16import markdown 17import os 18import pprint 19import re 20import tidylib 21import subprocess 22 23# TODO (gdimino): Clean up this code using templates 24# from jinja2 import Template 25 26HEADERS_FOR_TOC = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7'] 27TOC_PER_COL = 34 28 29def 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 63def 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 79def 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 84def 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 105def 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 112def create_id(header_tag): 113 return header_tag.contents[0].lower().replace('. ', '_').replace(' ', '_').replace('.', '_') 114 115# Utilities 116def 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 122def 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 131def 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 149def remove_space_before_punctuation(input): 150 space_before_punc = r'\s+([.,:])' 151 return re.sub(space_before_punc, '\1') 152 153def 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 180if __name__ == '__main__': 181 main() 182 183 184 185 186