]> Git Repo - binutils.git/blob - sim/common/gennltvals.py
Automatic date update in version.in
[binutils.git] / sim / common / gennltvals.py
1 #!/usr/bin/env python3
2 # Copyright (C) 1996-2022 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     gentvals(output_dir, cpp, 'errno', newlib / 'newlib/libc/include',
146              ('errno.h', 'sys/errno.h'), 'E[A-Z0-9]*')
147
148     gentvals(output_dir, cpp, 'signal', newlib / 'newlib/libc/include',
149              ('signal.h', 'sys/signal.h'), r'SIG[A-Z0-9]*', filter=r'SIGSTKSZ')
150
151     gentvals(output_dir, cpp, 'open', newlib / 'newlib/libc/include',
152              ('fcntl.h', 'sys/fcntl.h', 'sys/_default_fcntl.h'), r'O_[A-Z0-9]*')
153
154
155 def 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.
185     for target, subdir in sorted(TARGET_DIRS.items()):
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('')
203
204     # Then output the common syscall targets.
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
226 def gen_targets(output_dir: Path, newlib: Path, cpp: str):
227     """Generate the target-specific lists."""
228     gen_target_syscall(output_dir, newlib, cpp)
229
230
231 def gen(output_dir: Path, newlib: Path, cpp: str):
232     """Generate all the things!"""
233     gen_common(output_dir, newlib, cpp)
234     gen_targets(output_dir, newlib, cpp)
235
236
237 def 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,
244         help='write to the specified directory')
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
257 def 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
262     if opts.output is None:
263         # Default to where the script lives.
264         opts.output = Path(__file__).resolve().parent
265
266     if opts.srcroot is None:
267         opts.srcroot = Path(__file__).resolve().parent.parent.parent
268     else:
269         opts.srcroot = opts.srcroot.resolve()
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
288 def main(argv: List[str]) -> int:
289     """The main entry point for scripts."""
290     opts = parse_args(argv)
291
292     gen(opts.output, opts.newlib, opts.cpp)
293     return 0
294
295
296 if __name__ == '__main__':
297     sys.exit(main(sys.argv[1:]))
This page took 0.040403 seconds and 4 git commands to generate.