blob: 6848c60006be0d3f02e176b7fbb0663c876838d0 [file] [log] [blame]
Mike Frysingera23e81f2019-07-01 11:55:50 -04001#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2019 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"""Print errno table as markdown."""
8
9from __future__ import print_function
10
11import argparse
12import os
13import subprocess
14import sys
15
16import constants
17
18
19# The C library header to find symbols.
20HEADER = 'errno.h'
21
22# The markdown file where we store this table.
23MARKDOWN = 'errnos.md'
24
25# The string we use to start the table header.
26START_OF_TABLE = '| number |'
27
28
29def find_symbols(target):
30 """Find all the symbols using |target|."""
31 cc = '%s-clang' % (target,)
32 ret = subprocess.run([cc, '-E', '-dD', '-P', '-'],
33 input='#include <%s>\n' % (HEADER,),
34 stdout=subprocess.PIPE,
35 encoding='utf-8')
36 table = {}
37 for line in ret.stdout.splitlines():
38 # If we need to make this smarter, signals.py handles things.
39 assert not line.startswith('#undef E'), '#undef not handled'
40 if line.startswith('#define E'):
41 sym, val = line.strip().split()[1:3]
42 assert sym not in table, 'sym %s already found' % (sym,)
43 table[sym] = val
44 return table
45
46
47def load_table():
48 """Return a table of all the symbol values (and aliases)."""
49 all_tables = {}
50 for target in constants.TARGETS:
51 all_tables[target] = find_symbols(target)
52
53 # Sanity check that all the tables are the same.
54 basetarget = constants.TARGETS[0]
55 baseline = all_tables[basetarget]
56 for target, table in all_tables.items():
57 assert baseline == table
58
59 # Sometimes values have multiple names.
60 aliases = {}
61 for sym, val in baseline.items():
62 try:
63 int(val)
64 except ValueError:
65 aliases.setdefault(val, []).append(sym)
66
67 return (baseline, aliases)
68
69
70def sort_table(table):
71 """Return a sorted table."""
72 def sorter(element):
73 try:
74 num = int(element[1])
75 except ValueError:
76 num = 0
77 return (num, element[0])
78 return sorted(table.items(), key=sorter)
79
80
81def get_md_table(table, aliases):
82 """Return the table in markdown format."""
83 ret = []
84 last_num = 0
85 for sym, val in sort_table(table):
86 try:
87 num = int(val)
88 except ValueError:
89 continue
90
91 # Fill in holes in the table so it's obvious to the user when searching.
92 for stub in range(last_num + 1, num):
93 ret.append('| %i | 0x%02x | | *not implemented* ||' % (stub, stub))
94 last_num = num
95
96 desc = os.strerror(num)
97 ret.append('| %i | 0x%02x | %s | %s |' % (num, num, sym, desc))
98 for alias in aliases.get(sym, []):
99 ret.append('| %i | 0x%02x | %s | *(Same value as %s)* %s |' %
100 (num, num, alias, sym, desc))
101 return ret
102
103
104def get_parser():
105 """Return a command line parser."""
106 parser = argparse.ArgumentParser(description=__doc__)
107 parser.add_argument('-i', '--inplace', action='store_true',
108 help='Update the markdown file directly.')
109 return parser
110
111
112def main(argv):
113 """The main func!"""
114 parser = get_parser()
115 opts = parser.parse_args(argv)
116
117 baseline, aliases = load_table()
118 md_data = get_md_table(baseline, aliases)
119
120 if opts.inplace:
121 md_file = os.path.join(os.path.dirname(os.path.abspath(__file__)),
122 MARKDOWN)
123 with open(md_file) as fp:
124 old_data = fp.readlines()
125
126 i = None
127 for i, line in enumerate(old_data):
128 if line.startswith(START_OF_TABLE):
129 break
130 else:
131 print('ERROR: could not find table in %s' % (md_file,),
132 file=sys.stderr)
133 sys.exit(1)
134
135 old_data = old_data[0:i + 2]
136 with open(md_file, 'w') as fp:
137 fp.writelines(old_data)
138 fp.write('\n'.join(md_data) + '\n')
139 else:
140 print('\n'.join(md_data))
141
142
143if __name__ == '__main__':
144 sys.exit(main(sys.argv[1:]))