]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - sim/common/gennltvals.py
Update copyright year range in header of all files managed by GDB
[thirdparty/binutils-gdb.git] / sim / common / gennltvals.py
CommitLineData
9a7ba4aa 1#!/usr/bin/env python3
1d506c26 2# Copyright (C) 1996-2024 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 """
a70dcdeb
MF
145 # Enable Linux errno extensions since the newlib values are designed to
146 # not conflict with each other.
147 gentvals(output_dir,
148 cpp + ' -D__LINUX_ERRNO_EXTENSIONS__',
149 'errno', newlib / 'newlib/libc/include',
9a7ba4aa
MF
150 ('errno.h', 'sys/errno.h'), 'E[A-Z0-9]*')
151
1e42d017 152 gentvals(output_dir, cpp, 'signal', newlib / 'newlib/libc/include',
39d53d04 153 ('signal.h', 'sys/signal.h'), r'SIG[A-Z0-9]*', filter=r'SIGSTKSZ')
9a7ba4aa 154
1e42d017 155 gentvals(output_dir, cpp, 'open', newlib / 'newlib/libc/include',
9a7ba4aa
MF
156 ('fcntl.h', 'sys/fcntl.h', 'sys/_default_fcntl.h'), r'O_[A-Z0-9]*')
157
158
64ae70dd
MF
159def gen_target_syscall(output_dir: Path, newlib: Path, cpp: str):
160 """Generate the target-specific syscall lists."""
161 target_map_c = output_dir / 'target-newlib-syscall.c'
162 old_lines_c = target_map_c.read_text().splitlines()
163 start_i = end_i = None
164 for i, line in enumerate(old_lines_c):
165 if START_MARKER in line:
166 start_i = i
167 if END_MARKER in line:
168 end_i = i
169 assert start_i and end_i, f'{target_map_c}: Unable to find markers'
170 new_lines_c = old_lines_c[0:start_i + 1]
171 new_lines_c_end = old_lines_c[end_i:]
172
173 target_map_h = output_dir / 'target-newlib-syscall.h'
174 old_lines_h = target_map_h.read_text().splitlines()
175 start_i = end_i = None
176 for i, line in enumerate(old_lines_h):
177 if START_MARKER in line:
178 start_i = i
179 if END_MARKER in line:
180 end_i = i
181 assert start_i and end_i, f'{target_map_h}: Unable to find markers'
182 new_lines_h = old_lines_h[0:start_i + 1]
183 new_lines_h_end = old_lines_h[end_i:]
184
185 headers = ('syscall.h',)
186 pattern = r'SYS_[_a-zA-Z0-9]*'
187
188 # Output the target-specific syscalls.
bd0918c9 189 for target, subdir in sorted(TARGET_DIRS.items()):
64ae70dd
MF
190 syms = extract_syms(cpp, newlib / subdir, headers, pattern)
191 new_lines_c.append(f'CB_TARGET_DEFS_MAP cb_{target}_syscall_map[] = {{')
192 new_lines_c.extend(
193 f'#ifdef CB_{sym}\n'
194 ' { '
195 f'"{sym[4:]}", CB_{sym}, TARGET_NEWLIB_{target.upper()}_{sym}'
196 ' },\n'
197 '#endif' for sym in sorted(syms))
198 new_lines_c.append(' {NULL, -1, -1},')
199 new_lines_c.append('};\n')
200
201 new_lines_h.append(
202 f'extern CB_TARGET_DEFS_MAP cb_{target}_syscall_map[];')
203 new_lines_h.extend(
204 f'#define TARGET_NEWLIB_{target.upper()}_{sym} {val}'
205 for sym, val in sorted(syms.items()))
206 new_lines_h.append('')
9a7ba4aa 207
bd0918c9 208 # Then output the common syscall targets.
64ae70dd
MF
209 syms = extract_syms(cpp, newlib / 'libgloss', headers, pattern)
210 new_lines_c.append(f'CB_TARGET_DEFS_MAP cb_init_syscall_map[] = {{')
211 new_lines_c.extend(
212 f'#ifdef CB_{sym}\n'
213 f' {{ "{sym[4:]}", CB_{sym}, TARGET_NEWLIB_{sym} }},\n'
214 f'#endif' for sym in sorted(syms))
215 new_lines_c.append(' {NULL, -1, -1},')
216 new_lines_c.append('};')
217
218 new_lines_h.append('extern CB_TARGET_DEFS_MAP cb_init_syscall_map[];')
219 new_lines_h.extend(
220 f'#define TARGET_NEWLIB_{sym} {val}'
221 for sym, val in sorted(syms.items()))
222
223 new_lines_c.extend(new_lines_c_end)
224 target_map_c.write_text('\n'.join(new_lines_c) + '\n')
225
226 new_lines_h.extend(new_lines_h_end)
227 target_map_h.write_text('\n'.join(new_lines_h) + '\n')
228
229
1e42d017 230def gen_targets(output_dir: Path, newlib: Path, cpp: str):
64ae70dd
MF
231 """Generate the target-specific lists."""
232 gen_target_syscall(output_dir, newlib, cpp)
bd0918c9 233
9a7ba4aa 234
1e42d017 235def gen(output_dir: Path, newlib: Path, cpp: str):
9a7ba4aa 236 """Generate all the things!"""
1e42d017
MF
237 gen_common(output_dir, newlib, cpp)
238 gen_targets(output_dir, newlib, cpp)
9a7ba4aa
MF
239
240
241def get_parser() -> argparse.ArgumentParser:
242 """Get CLI parser."""
243 parser = argparse.ArgumentParser(
244 description=__doc__,
245 formatter_class=argparse.RawDescriptionHelpFormatter)
246 parser.add_argument(
247 '-o', '--output', type=Path,
082cf694 248 help='write to the specified directory')
9a7ba4aa
MF
249 parser.add_argument(
250 '--cpp', type=str, default='cpp',
251 help='the preprocessor to use')
252 parser.add_argument(
253 '--srcroot', type=Path,
254 help='the root of this source tree')
255 parser.add_argument(
256 'newlib', nargs='?', type=Path,
257 help='path to the newlib+libgloss source tree')
258 return parser
259
260
261def parse_args(argv: List[str]) -> argparse.Namespace:
262 """Process the command line & default options."""
263 parser = get_parser()
264 opts = parser.parse_args(argv)
265
082cf694
MF
266 if opts.output is None:
267 # Default to where the script lives.
268 opts.output = Path(__file__).resolve().parent
269
9a7ba4aa
MF
270 if opts.srcroot is None:
271 opts.srcroot = Path(__file__).resolve().parent.parent.parent
082cf694
MF
272 else:
273 opts.srcroot = opts.srcroot.resolve()
9a7ba4aa
MF
274
275 if opts.newlib is None:
276 # Try to find newlib relative to our source tree.
277 if (opts.srcroot / 'newlib').is_dir():
278 # If newlib is manually in the same source tree, use it.
279 if (opts.srcroot / 'libgloss').is_dir():
280 opts.newlib = opts.srcroot
281 else:
282 opts.newlib = opts.srcroot / 'newlib'
283 elif (opts.srcroot.parent / 'newlib').is_dir():
284 # Or see if it's alongside the gdb/binutils repo.
285 opts.newlib = opts.srcroot.parent / 'newlib'
286 if opts.newlib is None or not opts.newlib.is_dir():
287 parser.error('unable to find newlib')
288
289 return opts
290
291
292def main(argv: List[str]) -> int:
293 """The main entry point for scripts."""
294 opts = parse_args(argv)
295
1e42d017 296 gen(opts.output, opts.newlib, opts.cpp)
9a7ba4aa
MF
297 return 0
298
299
300if __name__ == '__main__':
301 sys.exit(main(sys.argv[1:]))