]>
Commit | Line | Data |
---|---|---|
9a7ba4aa MF |
1 | #!/usr/bin/env python3 |
2 | # Copyright (C) 1996-2021 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 nltvals.def. | |
20 | ||
21 | nltvals.def is a file that describes various newlib/libgloss target 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 | ||
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. | |
49 | TARGET_DIRS = { | |
50 | 'cr16': 'libgloss/cr16/sys', | |
51 | 'd10v': 'newlib/libc/sys/d10v/sys', | |
52 | 'i960': 'libgloss/i960', | |
53 | 'mcore': 'libgloss/mcore', | |
b9249c46 | 54 | 'riscv': 'libgloss/riscv/machine', |
b7c5246b | 55 | 'sh': 'newlib/libc/sys/sh/sys', |
9a7ba4aa MF |
56 | 'v850': 'libgloss/v850/sys', |
57 | } | |
9a7ba4aa | 58 | |
9a7ba4aa MF |
59 | |
60 | # The header for the generated def file. | |
61 | FILE_HEADER = f"""\ | |
62 | /* Newlib/libgloss macro values needed by remote target support. */ | |
63 | /* This file is machine generated by {PROG}. */\ | |
64 | """ | |
65 | ||
66 | ||
67 | def gentvals(output: TextIO, cpp: str, srctype: str, srcdir: Path, | |
68 | headers: Iterable[str], | |
69 | pattern: str, | |
70 | target: str = None): | |
71 | """Extract constants from the specified files using a regular expression. | |
72 | ||
73 | We'll run things through the preprocessor. | |
74 | """ | |
75 | headers = tuple(headers) | |
76 | ||
77 | # Require all files exist in order to regenerate properly. | |
78 | for header in headers: | |
79 | fullpath = srcdir / header | |
80 | assert fullpath.exists(), f'{fullpath} does not exist' | |
81 | ||
bd0918c9 | 82 | if target is not None: |
9a7ba4aa | 83 | print(f'#ifdef NL_TARGET_{target}', file=output) |
bd0918c9 | 84 | print(f'#ifdef {srctype}_defs', file=output) |
9a7ba4aa MF |
85 | |
86 | print('\n'.join(f'/* from {x} */' for x in headers), file=output) | |
87 | ||
88 | if target is None: | |
89 | print(f'/* begin {srctype} target macros */', file=output) | |
90 | else: | |
91 | print(f'/* begin {target} {srctype} target macros */', file=output) | |
92 | ||
93 | # Extract all the symbols. | |
94 | srcfile = ''.join(f'#include <{x}>\n' for x in headers) | |
95 | syms = set() | |
96 | define_pattern = re.compile(r'^#\s*define\s+(' + pattern + ')') | |
97 | for header in headers: | |
98 | with open(srcdir / header, 'r', encoding='utf-8') as fp: | |
99 | data = fp.read() | |
100 | for line in data.splitlines(): | |
101 | m = define_pattern.match(line) | |
102 | if m: | |
103 | syms.add(m.group(1)) | |
104 | for sym in sorted(syms): | |
105 | srcfile += f'#ifdef {sym}\nDEFVAL {{ "{sym}", {sym} }},\n#endif\n' | |
106 | ||
107 | result = subprocess.run( | |
108 | f'{cpp} -E -I"{srcdir}" -', shell=True, check=True, encoding='utf-8', | |
109 | input=srcfile, capture_output=True) | |
110 | for line in result.stdout.splitlines(): | |
111 | if line.startswith('DEFVAL '): | |
112 | print(line[6:].rstrip(), file=output) | |
113 | ||
bd0918c9 | 114 | print(f'#undef {srctype}_defs', file=output) |
9a7ba4aa MF |
115 | if target is None: |
116 | print(f'/* end {srctype} target macros */', file=output) | |
9a7ba4aa MF |
117 | else: |
118 | print(f'/* end {target} {srctype} target macros */', file=output) | |
119 | print('#endif', file=output) | |
bd0918c9 | 120 | print('#endif', file=output) |
9a7ba4aa MF |
121 | |
122 | ||
123 | def gen_common(output: TextIO, newlib: Path, cpp: str): | |
124 | """Generate the common C library constants. | |
125 | ||
126 | No arch should override these. | |
127 | """ | |
128 | gentvals(output, cpp, 'errno', newlib / 'newlib/libc/include', | |
129 | ('errno.h', 'sys/errno.h'), 'E[A-Z0-9]*') | |
130 | ||
131 | gentvals(output, cpp, 'signal', newlib / 'newlib/libc/include', | |
132 | ('signal.h', 'sys/signal.h'), r'SIG[A-Z0-9]*') | |
133 | ||
134 | gentvals(output, cpp, 'open', newlib / 'newlib/libc/include', | |
135 | ('fcntl.h', 'sys/fcntl.h', 'sys/_default_fcntl.h'), r'O_[A-Z0-9]*') | |
136 | ||
137 | ||
138 | def gen_targets(output: TextIO, newlib: Path, cpp: str): | |
139 | """Generate the target-specific lists.""" | |
bd0918c9 | 140 | for target, subdir in sorted(TARGET_DIRS.items()): |
9a7ba4aa MF |
141 | gentvals(output, cpp, 'sys', newlib / subdir, ('syscall.h',), |
142 | r'SYS_[_a-zA-Z0-9]*', target=target) | |
143 | ||
bd0918c9 MF |
144 | # Then output the common syscall targets. |
145 | gentvals(output, cpp, 'sys', newlib / 'libgloss', ('syscall.h',), | |
146 | r'SYS_[_a-zA-Z0-9]*') | |
147 | ||
9a7ba4aa MF |
148 | |
149 | def gen(output: TextIO, newlib: Path, cpp: str): | |
150 | """Generate all the things!""" | |
151 | print(FILE_HEADER, file=output) | |
152 | gen_common(output, newlib, cpp) | |
153 | gen_targets(output, newlib, cpp) | |
154 | ||
155 | ||
156 | def get_parser() -> argparse.ArgumentParser: | |
157 | """Get CLI parser.""" | |
158 | parser = argparse.ArgumentParser( | |
159 | description=__doc__, | |
160 | formatter_class=argparse.RawDescriptionHelpFormatter) | |
161 | parser.add_argument( | |
162 | '-o', '--output', type=Path, | |
082cf694 | 163 | help='write to the specified directory') |
9a7ba4aa MF |
164 | parser.add_argument( |
165 | '--cpp', type=str, default='cpp', | |
166 | help='the preprocessor to use') | |
167 | parser.add_argument( | |
168 | '--srcroot', type=Path, | |
169 | help='the root of this source tree') | |
170 | parser.add_argument( | |
171 | 'newlib', nargs='?', type=Path, | |
172 | help='path to the newlib+libgloss source tree') | |
173 | return parser | |
174 | ||
175 | ||
176 | def parse_args(argv: List[str]) -> argparse.Namespace: | |
177 | """Process the command line & default options.""" | |
178 | parser = get_parser() | |
179 | opts = parser.parse_args(argv) | |
180 | ||
082cf694 MF |
181 | if opts.output is None: |
182 | # Default to where the script lives. | |
183 | opts.output = Path(__file__).resolve().parent | |
184 | ||
9a7ba4aa MF |
185 | if opts.srcroot is None: |
186 | opts.srcroot = Path(__file__).resolve().parent.parent.parent | |
082cf694 MF |
187 | else: |
188 | opts.srcroot = opts.srcroot.resolve() | |
9a7ba4aa MF |
189 | |
190 | if opts.newlib is None: | |
191 | # Try to find newlib relative to our source tree. | |
192 | if (opts.srcroot / 'newlib').is_dir(): | |
193 | # If newlib is manually in the same source tree, use it. | |
194 | if (opts.srcroot / 'libgloss').is_dir(): | |
195 | opts.newlib = opts.srcroot | |
196 | else: | |
197 | opts.newlib = opts.srcroot / 'newlib' | |
198 | elif (opts.srcroot.parent / 'newlib').is_dir(): | |
199 | # Or see if it's alongside the gdb/binutils repo. | |
200 | opts.newlib = opts.srcroot.parent / 'newlib' | |
201 | if opts.newlib is None or not opts.newlib.is_dir(): | |
202 | parser.error('unable to find newlib') | |
203 | ||
204 | return opts | |
205 | ||
206 | ||
207 | def main(argv: List[str]) -> int: | |
208 | """The main entry point for scripts.""" | |
209 | opts = parse_args(argv) | |
210 | ||
082cf694 | 211 | output = (opts.output / 'nltvals.def').open('w', encoding='utf-8') |
9a7ba4aa MF |
212 | |
213 | gen(output, opts.newlib, opts.cpp) | |
214 | return 0 | |
215 | ||
216 | ||
217 | if __name__ == '__main__': | |
218 | sys.exit(main(sys.argv[1:])) |