1#!/usr/bin/env python 2# Copyright 2014 The Chromium Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Builds the complete main.html file from the basic components. 7""" 8 9from HTMLParser import HTMLParser 10import argparse 11import os 12import re 13import sys 14 15 16def error(msg): 17 print 'Error: %s' % msg 18 sys.exit(1) 19 20 21class HtmlChecker(HTMLParser): 22 def __init__(self): 23 HTMLParser.__init__(self) 24 self.ids = set() 25 26 def handle_starttag(self, tag, attrs): 27 for (name, value) in attrs: 28 if name == 'id': 29 if value in self.ids: 30 error('Duplicate id: %s' % value) 31 self.ids.add(value) 32 33 34class GenerateWebappHtml: 35 def __init__(self, template_files, js_files, instrumented_js_files, 36 template_rel_dir): 37 38 self.js_files = js_files 39 self.instrumented_js_files = instrumented_js_files 40 self.template_rel_dir = template_rel_dir 41 42 self.templates_expected = set() 43 for template in template_files: 44 self.templates_expected.add(os.path.basename(template)) 45 46 self.templates_found = set() 47 48 def includeJavascript(self, output): 49 for js_path in sorted(self.js_files): 50 js_file = os.path.basename(js_path) 51 output.write(' <script src="' + js_file + '"></script>\n') 52 53 for js_path in sorted(self.instrumented_js_files): 54 js_file = os.path.basename(js_path) 55 output.write(' <script src="' + js_file + '" data-cover></script>\n') 56 57 def verifyTemplateList(self): 58 """Verify that all the expected templates were found.""" 59 if self.templates_expected > self.templates_found: 60 extra = self.templates_expected - self.templates_found 61 print 'Extra templates specified:', extra 62 return False 63 return True 64 65 def validateTemplate(self, template_path): 66 template = os.path.basename(template_path) 67 if template in self.templates_expected: 68 self.templates_found.add(template) 69 return True 70 return False 71 72 def processTemplate(self, output, template_file, indent): 73 with open(os.path.join(self.template_rel_dir, template_file), 'r') as \ 74 input_template: 75 first_line = True 76 skip_header_comment = False 77 78 for line in input_template: 79 # If the first line is the start of a copyright notice, then 80 # skip over the entire comment. 81 # This will remove the copyright info from the included files, 82 # but leave the one on the main template. 83 if first_line and re.match(r'<!--', line): 84 skip_header_comment = True 85 first_line = False 86 if skip_header_comment: 87 if re.search(r'-->', line): 88 skip_header_comment = False 89 continue 90 91 m = re.match( 92 r'^(\s*)<meta-include src="(.+)"\s*/>\s*$', 93 line) 94 if m: 95 prefix = m.group(1) 96 template_name = m.group(2) 97 if not self.validateTemplate(template_name): 98 error('Found template not in list of expected templates: %s' % 99 template_name) 100 self.processTemplate(output, template_name, indent + len(prefix)) 101 continue 102 103 m = re.match(r'^\s*<meta-include type="javascript"\s*/>\s*$', line) 104 if m: 105 self.includeJavascript(output) 106 continue 107 108 if line.strip() == '': 109 output.write('\n') 110 else: 111 output.write((' ' * indent) + line) 112 113 114def parseArgs(): 115 parser = argparse.ArgumentParser() 116 parser.add_argument( 117 '--js', nargs='+', help='The Javascript files to include in HTML <head>') 118 parser.add_argument( 119 '--templates', 120 nargs='*', 121 default=[], 122 help='The html template files used by input-template') 123 parser.add_argument( 124 '--exclude-js', 125 nargs='*', 126 default=[], 127 help='The Javascript files to exclude from <--js> and <--instrumentedjs>') 128 parser.add_argument( 129 '--instrument-js', 130 nargs='*', 131 default=[], 132 help='Javascript to include and instrument for code coverage') 133 parser.add_argument( 134 '--dir-for-templates', 135 default = ".", 136 help='Directory template references in html are relative to') 137 parser.add_argument('output_file') 138 parser.add_argument('input_template') 139 return parser.parse_args(sys.argv[1:]) 140 141 142def main(): 143 args = parseArgs() 144 145 out_file = args.output_file 146 js_files = set(args.js) - set(args.exclude_js) 147 148 # Create the output directory if it does not exist. 149 out_directory = os.path.dirname(out_file) 150 if not os.path.exists(out_directory): 151 os.makedirs(out_directory) 152 153 # Generate the main HTML file from the templates. 154 with open(out_file, 'w') as output: 155 gen = GenerateWebappHtml(args.templates, js_files, args.instrument_js, 156 args.dir_for_templates) 157 gen.processTemplate(output, args.input_template, 0) 158 159 # Verify that all the expected templates were found. 160 if not gen.verifyTemplateList(): 161 error('Extra templates specified') 162 163 # Verify that the generated HTML file is valid. 164 with open(out_file, 'r') as input_html: 165 parser = HtmlChecker() 166 parser.feed(input_html.read()) 167 168 169if __name__ == '__main__': 170 sys.exit(main()) 171