]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - sim/common/gennltvals.py
Automatic date update in version.in
[thirdparty/binutils-gdb.git] / sim / common / gennltvals.py
CommitLineData
9a7ba4aa 1#!/usr/bin/env python3
213516ef 2# Copyright (C) 1996-2023 Free Software Foundation, Inc.
9a7ba4aa
MF
3#
4# This file is part of the GNU simulators.
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18
1e42d017 19"""Helper to generate target-newlib-* files.
9a7ba4aa 20
1e42d017 21target-newlib-* are files that describe various newlib/libgloss values used
9a7ba4aa
MF
22by the host/target interface. This needs to be rerun whenever the newlib source
23changes. Developers manually run it.
24
25If the path to newlib is not specified, it will be searched for in:
26- the root of this source tree
27- alongside this source tree
28"""
29
30import argparse
31from pathlib import Path
32import re
33import subprocess
34import sys
35from typing import Iterable, List, TextIO
36
37
38PROG = Path(__file__).name
39
bd0918c9 40# Unfortunately, many newlib/libgloss ports have seen fit to define their own
9a7ba4aa
MF
41# syscall.h file. This means that system call numbers can vary for each port.
42# Support for all this crud is kept here, rather than trying to get too fancy.
43# If you want to try to improve this, please do, but don't break anything.
bd0918c9
MF
44#
45# If a target isn't listed here, it gets the standard syscall.h file (see
46# libgloss/syscall.h) which hopefully new targets will use.
9a7ba4aa
MF
47#
48# NB: New ports should use libgloss, not newlib.
49TARGET_DIRS = {
50 'cr16': 'libgloss/cr16/sys',
51 'd10v': 'newlib/libc/sys/d10v/sys',
d485f8c7
MF
52 # Port removed from the tree years ago.
53 #'i960': 'libgloss/i960',
9a7ba4aa 54 'mcore': 'libgloss/mcore',
b9249c46 55 'riscv': 'libgloss/riscv/machine',
b7c5246b 56 'sh': 'newlib/libc/sys/sh/sys',
9a7ba4aa
MF
57 'v850': 'libgloss/v850/sys',
58}
9a7ba4aa 59
9a7ba4aa
MF
60
61# The header for the generated def file.
62FILE_HEADER = f"""\
63/* Newlib/libgloss macro values needed by remote target support. */
64/* This file is machine generated by {PROG}. */\
65"""
66
a7e40a99
MF
67# Used to update sections of files.
68START_MARKER = 'gennltvals: START'
69END_MARKER = 'gennltvals: END'
9a7ba4aa 70
a7e40a99
MF
71
72def extract_syms(cpp: str, srcdir: Path,
73 headers: Iterable[str],
74 pattern: str,
75 filter: str = r'^$') -> dict:
76 """Extract all the symbols from |headers| matching |pattern| using |cpp|."""
77 srcfile = ''.join(f'#include <{x}>\n' for x in headers)
78 syms = set()
79 define_pattern = re.compile(r'^#\s*define\s+(' + pattern + ')')
80 filter_pattern = re.compile(filter)
81 for header in headers:
82 with open(srcdir / header, 'r', encoding='utf-8') as fp:
83 data = fp.read()
84 for line in data.splitlines():
85 m = define_pattern.match(line)
86 if m and not filter_pattern.search(line):
87 syms.add(m.group(1))
88 for sym in syms:
89 srcfile += f'#ifdef {sym}\nDEFVAL "{sym}" {sym}\n#endif\n'
90
91 result = subprocess.run(
92 f'{cpp} -E -I"{srcdir}" -', shell=True, check=True, encoding='utf-8',
93 input=srcfile, capture_output=True)
94 ret = {}
95 for line in result.stdout.splitlines():
96 if line.startswith('DEFVAL '):
97 _, sym, val = line.split()
98 ret[sym.strip('"')] = val
99 return ret
100
101
1e42d017 102def gentvals(output_dir: Path,
a7e40a99 103 cpp: str, srctype: str, srcdir: Path,
9a7ba4aa
MF
104 headers: Iterable[str],
105 pattern: str,
39d53d04 106 filter: str = r'^$',
9a7ba4aa
MF
107 target: str = None):
108 """Extract constants from the specified files using a regular expression.
109
110 We'll run things through the preprocessor.
111 """
112 headers = tuple(headers)
113
114 # Require all files exist in order to regenerate properly.
115 for header in headers:
116 fullpath = srcdir / header
117 assert fullpath.exists(), f'{fullpath} does not exist'
118
a7e40a99
MF
119 syms = extract_syms(cpp, srcdir, headers, pattern, filter)
120
a7e40a99 121 target_map = output_dir / f'target-newlib-{srctype}.c'
1e42d017
MF
122 assert target_map.exists(), f'{target_map}: Missing skeleton'
123 old_lines = target_map.read_text().splitlines()
124 start_i = end_i = None
125 for i, line in enumerate(old_lines):
126 if START_MARKER in line:
127 start_i = i
128 if END_MARKER in line:
129 end_i = i
130 assert start_i and end_i
131 new_lines = old_lines[0:start_i + 1]
132 new_lines.extend(
133 f'#ifdef {sym}\n'
134 f' {{ "{sym}", {sym}, {val} }},\n'
135 f'#endif' for sym, val in sorted(syms.items()))
136 new_lines.extend(old_lines[end_i:])
137 target_map.write_text('\n'.join(new_lines) + '\n')
9a7ba4aa
MF
138
139
1e42d017 140def gen_common(output_dir: Path, newlib: Path, cpp: str):
9a7ba4aa
MF
141 """Generate the common C library constants.
142
143 No arch should override these.
144 """
1e42d017 145 gentvals(output_dir, cpp, 'errno', newlib / 'newlib/libc/include',
9a7ba4aa
MF
146 ('errno.h', 'sys/errno.h'), 'E[A-Z0-9]*')
147
1e42d017 148 gentvals(output_dir, cpp, 'signal', newlib / 'newlib/libc/include',
39d53d04 149 ('signal.h', 'sys/signal.h'), r'SIG[A-Z0-9]*', filter=r'SIGSTKSZ')
9a7ba4aa 150
1e42d017 151 gentvals(output_dir, cpp, 'open', newlib / 'newlib/libc/include',
9a7ba4aa
MF
152 ('fcntl.h', 'sys/fcntl.h', 'sys/_default_fcntl.h'), r'O_[A-Z0-9]*')
153
154
64ae70dd
MF
155def gen_target_syscall(output_dir: Path, newlib: Path, cpp: str):
156 """Generate the target-specific syscall lists."""
157 target_map_c = output_dir / 'target-newlib-syscall.c'
158 old_lines_c = target_map_c.read_text().splitlines()
159 start_i = end_i = None
160 for i, line in enumerate(old_lines_c):
161 if START_MARKER in line:
162 start_i = i
163 if END_MARKER in line:
164 end_i = i
165 assert start_i and end_i, f'{target_map_c}: Unable to find markers'
166 new_lines_c = old_lines_c[0:start_i + 1]
167 new_lines_c_end = old_lines_c[end_i:]
168
169 target_map_h = output_dir / 'target-newlib-syscall.h'
170 old_lines_h = target_map_h.read_text().splitlines()
171 start_i = end_i = None
172 for i, line in enumerate(old_lines_h):
173 if START_MARKER in line:
174 start_i = i
175 if END_MARKER in line:
176 end_i = i
177 assert start_i and end_i, f'{target_map_h}: Unable to find markers'
178 new_lines_h = old_lines_h[0:start_i + 1]
179 new_lines_h_end = old_lines_h[end_i:]
180
181 headers = ('syscall.h',)
182 pattern = r'SYS_[_a-zA-Z0-9]*'
183
184 # Output the target-specific syscalls.
bd0918c9 185 for target, subdir in sorted(TARGET_DIRS.items()):
64ae70dd
MF
186 syms = extract_syms(cpp, newlib / subdir, headers, pattern)
187 new_lines_c.append(f'CB_TARGET_DEFS_MAP cb_{target}_syscall_map[] = {{')
188 new_lines_c.extend(
189 f'#ifdef CB_{sym}\n'
190 ' { '
191 f'"{sym[4:]}", CB_{sym}, TARGET_NEWLIB_{target.upper()}_{sym}'
192 ' },\n'
193 '#endif' for sym in sorted(syms))
194 new_lines_c.append(' {NULL, -1, -1},')
195 new_lines_c.append('};\n')
196
197 new_lines_h.append(
198 f'extern CB_TARGET_DEFS_MAP cb_{target}_syscall_map[];')
199 new_lines_h.extend(
200 f'#define TARGET_NEWLIB_{target.upper()}_{sym} {val}'
201 for sym, val in sorted(syms.items()))
202 new_lines_h.append('')
9a7ba4aa 203
bd0918c9 204 # Then output the common syscall targets.
64ae70dd
MF
205 syms = extract_syms(cpp, newlib / 'libgloss', headers, pattern)
206 new_lines_c.append(f'CB_TARGET_DEFS_MAP cb_init_syscall_map[] = {{')
207 new_lines_c.extend(
208 f'#ifdef CB_{sym}\n'
209 f' {{ "{sym[4:]}", CB_{sym}, TARGET_NEWLIB_{sym} }},\n'
210 f'#endif' for sym in sorted(syms))
211 new_lines_c.append(' {NULL, -1, -1},')
212 new_lines_c.append('};')
213
214 new_lines_h.append('extern CB_TARGET_DEFS_MAP cb_init_syscall_map[];')
215 new_lines_h.extend(
216 f'#define TARGET_NEWLIB_{sym} {val}'
217 for sym, val in sorted(syms.items()))
218
219 new_lines_c.extend(new_lines_c_end)
220 target_map_c.write_text('\n'.join(new_lines_c) + '\n')
221
222 new_lines_h.extend(new_lines_h_end)
223 target_map_h.write_text('\n'.join(new_lines_h) + '\n')
224
225
1e42d017 226def gen_targets(output_dir: Path, newlib: Path, cpp: str):
64ae70dd
MF
227 """Generate the target-specific lists."""
228 gen_target_syscall(output_dir, newlib, cpp)
bd0918c9 229
9a7ba4aa 230
1e42d017 231def gen(output_dir: Path, newlib: Path, cpp: str):
9a7ba4aa 232 """Generate all the things!"""
1e42d017
MF
233 gen_common(output_dir, newlib, cpp)
234 gen_targets(output_dir, newlib, cpp)
9a7ba4aa
MF
235
236
237def get_parser() -> argparse.ArgumentParser:
238 """Get CLI parser."""
239 parser = argparse.ArgumentParser(
240 description=__doc__,
241 formatter_class=argparse.RawDescriptionHelpFormatter)
242 parser.add_argument(
243 '-o', '--output', type=Path,
082cf694 244 help='write to the specified directory')
9a7ba4aa
MF
245 parser.add_argument(
246 '--cpp', type=str, default='cpp',
247 help='the preprocessor to use')
248 parser.add_argument(
249 '--srcroot', type=Path,
250 help='the root of this source tree')
251 parser.add_argument(
252 'newlib', nargs='?', type=Path,
253 help='path to the newlib+libgloss source tree')
254 return parser
255
256
257def parse_args(argv: List[str]) -> argparse.Namespace:
258 """Process the command line & default options."""
259 parser = get_parser()
260 opts = parser.parse_args(argv)
261
082cf694
MF
262 if opts.output is None:
263 # Default to where the script lives.
264 opts.output = Path(__file__).resolve().parent
265
9a7ba4aa
MF
266 if opts.srcroot is None:
267 opts.srcroot = Path(__file__).resolve().parent.parent.parent
082cf694
MF
268 else:
269 opts.srcroot = opts.srcroot.resolve()
9a7ba4aa
MF
270
271 if opts.newlib is None:
272 # Try to find newlib relative to our source tree.
273 if (opts.srcroot / 'newlib').is_dir():
274 # If newlib is manually in the same source tree, use it.
275 if (opts.srcroot / 'libgloss').is_dir():
276 opts.newlib = opts.srcroot
277 else:
278 opts.newlib = opts.srcroot / 'newlib'
279 elif (opts.srcroot.parent / 'newlib').is_dir():
280 # Or see if it's alongside the gdb/binutils repo.
281 opts.newlib = opts.srcroot.parent / 'newlib'
282 if opts.newlib is None or not opts.newlib.is_dir():
283 parser.error('unable to find newlib')
284
285 return opts
286
287
288def main(argv: List[str]) -> int:
289 """The main entry point for scripts."""
290 opts = parse_args(argv)
291
1e42d017 292 gen(opts.output, opts.newlib, opts.cpp)
9a7ba4aa
MF
293 return 0
294
295
296if __name__ == '__main__':
297 sys.exit(main(sys.argv[1:]))