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