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