summaryrefslogtreecommitdiffstats
path: root/remoting/webapp/build-html.py
blob: 8973b6f06cb3668599ca5aa0bc9dda5a913d9388 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#!/usr/bin/env python
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Builds the complete main.html file from the basic components.
"""

from HTMLParser import HTMLParser
import argparse
import os
import re
import sys


def error(msg):
  print 'Error: %s' % msg
  sys.exit(1)


class HtmlChecker(HTMLParser):
  def __init__(self):
    HTMLParser.__init__(self)
    self.ids = set()

  def handle_starttag(self, tag, attrs):
    for (name, value) in attrs:
      if name == 'id':
        if value in self.ids:
          error('Duplicate id: %s' % value)
        self.ids.add(value)


class GenerateWebappHtml:
  def __init__(self, template_files, js_files, instrumented_js_files,
               template_rel_dir):

    self.js_files = js_files
    self.instrumented_js_files = instrumented_js_files
    self.template_rel_dir = template_rel_dir

    self.templates_expected = set()
    for template in template_files:
      self.templates_expected.add(os.path.basename(template))

    self.templates_found = set()

  def includeJavascript(self, output):
    for js_file in sorted([os.path.basename(x) for x in self.js_files]):
      output.write('    <script src="' + js_file + '"></script>\n')

    for js_path in sorted(self.instrumented_js_files):
      js_file = os.path.basename(js_path)
      output.write('    <script src="' + js_file + '" data-cover></script>\n')

  def verifyTemplateList(self):
    """Verify that all the expected templates were found."""
    if self.templates_expected > self.templates_found:
      extra = self.templates_expected - self.templates_found
      print 'Extra templates specified:', extra
      return False
    return True

  def validateTemplate(self, template_path):
    template = os.path.basename(template_path)
    if template in self.templates_expected:
      self.templates_found.add(template)
      return True
    return False

  def processTemplate(self, output, template_file, indent):
    with open(template_file, 'r') as input_template:
      first_line = True
      skip_header_comment = False

      for line in input_template:
        # If the first line is the start of a copyright notice, then
        # skip over the entire comment.
        # This will remove the copyright info from the included files,
        # but leave the one on the main template.
        if first_line and re.match(r'<!--', line):
          skip_header_comment = True
        first_line = False
        if skip_header_comment:
          if re.search(r'-->', line):
            skip_header_comment = False
          continue

        m = re.match(
            r'^(\s*)<meta-include src="(.+)"\s*/>\s*$',
            line)
        if m:
          prefix = m.group(1)
          template_name = m.group(2)
          template_path = os.path.join(self.template_rel_dir, template_name)
          if not self.validateTemplate(template_path):
            error('Found template not in list of expected templates: %s' %
                  template_name)
          self.processTemplate(output, template_path, indent + len(prefix))
          continue

        m = re.match(r'^\s*<meta-include type="javascript"\s*/>\s*$', line)
        if m:
          self.includeJavascript(output)
          continue

        if line.strip() == '':
          output.write('\n')
        else:
          output.write((' ' * indent) + line)


def parseArgs():
  parser = argparse.ArgumentParser()
  parser.add_argument(
    '--js',
    nargs='+',
    default={},
    help='The Javascript files to include in HTML <head>')
  parser.add_argument(
    '--js-list-file',
    help='The name of a file containing a list of files, one per line, '
         'identifying the Javascript to include in HTML <head>. This is an '
         'alternate to specifying the files directly via the "--js" option. '
         'The files listed in this file are appended to the files passed via '
         'the "--js" option, if any.')
  parser.add_argument(
    '--templates',
    nargs='*',
    default=[],
    help='The html template files used by input-template')
  parser.add_argument(
    '--exclude-js',
    nargs='*',
    default=[],
    help='The Javascript files to exclude from <--js> and <--instrumentedjs>')
  parser.add_argument(
    '--instrument-js',
    nargs='*',
    default=[],
    help='Javascript to include and instrument for code coverage')
  parser.add_argument(
    '--template-dir',
    default = ".",
    help='Directory template references in html are relative to')
  parser.add_argument('output_file')
  parser.add_argument('input_template')
  return parser.parse_args(sys.argv[1:])


def main():
  args = parseArgs()

  out_file = args.output_file
  js_files = set(args.js)

  # Load the files from the --js-list-file.
  js_list_file = args.js_list_file
  if js_list_file:
    js_files = js_files.union(set(line.rstrip() for line in open(js_list_file)))

  js_files = js_files - set(args.exclude_js)
  instrumented_js_files = set(args.instrument_js) - set(args.exclude_js)

  # Create the output directory if it does not exist.
  out_directory = os.path.dirname(out_file)
  if out_directory is not '' and not os.path.exists(out_directory):
    os.makedirs(out_directory)

  # Generate the main HTML file from the templates.
  with open(out_file, 'w') as output:
    gen = GenerateWebappHtml(args.templates, js_files, instrumented_js_files,
                             args.template_dir)
    gen.processTemplate(output, args.input_template, 0)

    # Verify that all the expected templates were found.
    if not gen.verifyTemplateList():
      error('Extra templates specified')

  # Verify that the generated HTML file is valid.
  with open(out_file, 'r') as input_html:
    parser = HtmlChecker()
    parser.feed(input_html.read())


if __name__ == '__main__':
  sys.exit(main())