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