1#!/usr/bin/python
2
3# Copyright 2015 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""A Main file for command and structure generators.
8
9Takes in structures.txt and commands.txt as outputted by extract_*.sh, then
10passes files as input to structure_generator and command_generator objects.
11"""
12
13from __future__ import print_function
14
15import os
16import re
17import subprocess
18import sys
19
20import command_generator
21import extract_structures
22import structure_generator
23
24TMP_DIR = '/tmp'
25TYPES_FILE = 'tpm_types.h'
26
27
28class GeneratorException(Exception):
29  """Generator error, a convenience class."""
30  pass
31
32usage = ('''
33usage: %s [-h|[tar_archive|part2.html part3.html]]
34
35    -h  show this message and exit
36
37    tar_archive - a tarred archive consisting of at least two HTML files,
38                  parts 2 and 3 of the TCG TPM2 library specification. File
39                  names must include 'part2' and 'part3'. The extracted files
40                  could be found in %s after this script finished processing.
41
42    part{23}.html - parts 2 and 3 of the TCG TPM2 library specification in
43                    html format.
44''' % (os.path.basename(__file__), TMP_DIR))
45
46
47def _TryUntarring(tar_file_name):
48  """Try retrieving parts 2 and 3 from the passed in archive.
49
50  Args:
51    tar_file_name: a string, file name of the tar file which is supposed to
52        contain parts 2 and 3 of the specification.
53
54  Returns:
55    A tuple of strings, two file names in case they were found in the archive
56    and successfully extracted.
57  """
58  part2 = None
59  part3 = None
60  tar_extract_base = ['tar', '-C', TMP_DIR, '-f']
61
62  components = subprocess.check_output(['tar', 'tf', tar_file_name],
63                                       stderr=subprocess.STDOUT)
64  for name in components.splitlines():
65    if re.search('part2', name, re.IGNORECASE):
66      subprocess.check_output(tar_extract_base + [tar_file_name, '-x', name],
67                              stderr=subprocess.STDOUT)
68      part2 = os.path.join(TMP_DIR, name)
69    if re.search('part3', name, re.IGNORECASE):
70      subprocess.check_output(tar_extract_base + [tar_file_name, '-x', name],
71                              stderr=subprocess.STDOUT)
72      part3 = os.path.join(TMP_DIR, name)
73  return part2, part3
74
75
76def _ParseCommandLine(args):
77
78  """Process command line and determine input file names.
79
80  Input files could be supplied by two different ways - as part of a tar
81  archive (in which case only one command line parameter is expected), or as
82  two separate file names, one for part 2 and one for part 3.
83
84  If a single command line parameter is supplied, and it is not '-h', tar
85  extraction is attempted and if successful, two separate files are created in
86  TMP_DIR.
87
88  Args:
89    args: a list of string, command line parameters retrieved from sys.argv
90
91  Returns:
92    A tuple of two strings, two html files to process, part 2 and part 3 of
93    the spec.
94
95  Raises:
96    GeneratorException: on input errors.
97  """
98  if len(args) == 1:
99    if args[0] == '-h':
100      print(usage)
101      sys.exit(0)
102    try:
103      structures_file, commands_file = _TryUntarring(args[0])
104    except subprocess.CalledProcessError as e:
105      raise GeneratorException("command '%s' failed:\n%s\n%s" %
106                               (' '.join(e.cmd), e.output, usage))
107
108  elif len(args) == 2:
109    structures_file = args[0]
110    commands_file = args[1]
111  else:
112    raise GeneratorException(usage)
113  return structures_file, commands_file
114
115
116def main(argv):
117  """A Main function.
118
119  TPM structures and commands files are parsed and C header and C implementation
120  files are generated.
121
122  Args:
123    argv: a list of strings, command line parameters.
124  """
125
126  structures_file, commands_file = _ParseCommandLine(argv[1:])
127  print('parse part2...')
128  html_parser = extract_structures.SpecParser()
129  tpm_table = html_parser.GetTable()
130  # The tables included in the below tuple are defined twice in the
131  # specification, once in part 2 and once in part 4. Let's ignore the part 2
132  # definitions to avoid collisions.
133  tpm_table.SetSkipTables((2, 6, 9, 10, 13))
134  html_parser.feed(open(structures_file).read())
135  html_parser.close()
136  tpm_defines = tpm_table.GetHFile()
137
138  print('parse part3...')
139  tpm_table.SetSkipTables(())
140  html_parser.feed(open(commands_file).read())
141  html_parser.close()
142
143  # Move to the root directory, which is one level above the script.
144  os.chdir(os.path.join(os.path.dirname(argv[0]), '..'))
145
146  # Save types include file.
147  print('generate output...')
148  types_file = open(TYPES_FILE, 'w')
149  guard_name = TYPES_FILE.upper()
150  guard_name = guard_name.replace('.', '_')
151  guard_name = 'TPM2_' + guard_name + '_'
152  types_file.write((structure_generator.COPYRIGHT_HEADER +
153                    structure_generator.HEADER_FILE_GUARD_HEADER) %
154                   {'name': guard_name})
155  types_file.write(tpm_defines)
156  types_file.write((structure_generator.HEADER_FILE_GUARD_FOOTER) %
157                   {'name': guard_name})
158  types_file.close()
159  typemap = tpm_table.GetTypeMap()
160  structure_generator.GenerateHeader(typemap)
161  structure_generator.GenerateImplementation(typemap)
162  commands = tpm_table.GetCommandList()
163  command_generator.GenerateHeader(commands)
164  command_generator.GenerateImplementation(commands, typemap)
165  print('Processed %d TPM types.' % len(typemap))
166  print('Processed %d commands.' % len(commands))
167
168if __name__ == '__main__':
169  try:
170    main(sys.argv)
171  except GeneratorException as e:
172    if e.message:
173      print(e, file=sys.stderr)
174      sys.exit(1)
175