]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/common/gennltvals.py
Update copyright year range in header of all files managed by GDB
[thirdparty/binutils-gdb.git] / sim / common / gennltvals.py
1 #!/usr/bin/env python3
2 # Copyright (C) 1996-2024 Free Software Foundation, Inc.
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
19 """Helper to generate target-newlib-* files.
20
21 target-newlib-* are files that describe various newlib/libgloss values used
22 by the host/target interface. This needs to be rerun whenever the newlib source
23 changes. Developers manually run it.
24
25 If 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
30 import argparse
31 from pathlib import Path
32 import re
33 import subprocess
34 import sys
35 from typing import Iterable, List, TextIO
36
37
38 PROG = Path(__file__).name
39
40 # Unfortunately, many newlib/libgloss ports have seen fit to define their own
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.
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.
47 #
48 # NB: New ports should use libgloss, not newlib.
49 TARGET_DIRS = {
50 'cr16': 'libgloss/cr16/sys',
51 'd10v': 'newlib/libc/sys/d10v/sys',
52 # Port removed from the tree years ago.
53 #'i960': 'libgloss/i960',
54 'mcore': 'libgloss/mcore',
55 'riscv': 'libgloss/riscv/machine',
56 'sh': 'newlib/libc/sys/sh/sys',
57 'v850': 'libgloss/v850/sys',
58 }
59
60
61 # The header for the generated def file.
62 FILE_HEADER = f"""\
63 /* Newlib/libgloss macro values needed by remote target support. */
64 /* This file is machine generated by {PROG}. */\
65 """
66
67 # Used to update sections of files.
68 START_MARKER = 'gennltvals: START'
69 END_MARKER = 'gennltvals: END'
70
71
72 def 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
102 def gentvals(output_dir: Path,
103 cpp: str, srctype: str, srcdir: Path,
104 headers: Iterable[str],
105 pattern: str,
106 filter: str = r'^$',
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
119 syms = extract_syms(cpp, srcdir, headers, pattern, filter)
120
121 target_map = output_dir / f'target-newlib-{srctype}.c'
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')
138
139
140 def gen_common(output_dir: Path, newlib: Path, cpp: str):
141 """Generate the common C library constants.
142
143 No arch should override these.
144 """
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',
150 ('errno.h', 'sys/errno.h'), 'E[A-Z0-9]*')
151
152 gentvals(output_dir, cpp, 'signal', newlib / 'newlib/libc/include',
153 ('signal.h', 'sys/signal.h'), r'SIG[A-Z0-9]*', filter=r'SIGSTKSZ')
154
155 gentvals(output_dir, cpp, 'open', newlib / 'newlib/libc/include',
156 ('fcntl.h', 'sys/fcntl.h', 'sys/_default_fcntl.h'), r'O_[A-Z0-9]*')
157
158
159 def 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.
189 for target, subdir in sorted(TARGET_DIRS.items()):
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('')
207
208 # Then output the common syscall targets.
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
230 def gen_targets(output_dir: Path, newlib: Path, cpp: str):
231 """Generate the target-specific lists."""
232 gen_target_syscall(output_dir, newlib, cpp)
233
234
235 def gen(output_dir: Path, newlib: Path, cpp: str):
236 """Generate all the things!"""
237 gen_common(output_dir, newlib, cpp)
238 gen_targets(output_dir, newlib, cpp)
239
240
241 def 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,
248 help='write to the specified directory')
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
261 def 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
266 if opts.output is None:
267 # Default to where the script lives.
268 opts.output = Path(__file__).resolve().parent
269
270 if opts.srcroot is None:
271 opts.srcroot = Path(__file__).resolve().parent.parent.parent
272 else:
273 opts.srcroot = opts.srcroot.resolve()
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
292 def main(argv: List[str]) -> int:
293 """The main entry point for scripts."""
294 opts = parse_args(argv)
295
296 gen(opts.output, opts.newlib, opts.cpp)
297 return 0
298
299
300 if __name__ == '__main__':
301 sys.exit(main(sys.argv[1:]))