2 # Copyright (C) 1996-2023 Free Software Foundation, Inc.
4 # This file is part of the GNU simulators.
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.
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.
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/>.
19 """Helper to generate target-newlib-* files.
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.
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
31 from pathlib
import Path
35 from typing
import Iterable
, List
, TextIO
38 PROG
= Path(__file__
).name
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.
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.
48 # NB: New ports should use libgloss, not newlib.
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',
61 # The header for the generated def file.
63 /* Newlib/libgloss macro values needed by remote target support. */
64 /* This file is machine generated by {PROG}. */\
67 # Used to update sections of files.
68 START_MARKER
= 'gennltvals: START'
69 END_MARKER
= 'gennltvals: END'
72 def extract_syms(cpp
: str, srcdir
: Path
,
73 headers
: Iterable
[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
)
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
:
84 for line
in data
.splitlines():
85 m
= define_pattern
.match(line
)
86 if m
and not filter_pattern
.search(line
):
89 srcfile
+= f
'#ifdef {sym}\nDEFVAL "{sym}" {sym}\n#endif\n'
91 result
= subprocess
.run(
92 f
'{cpp} -E -I"{srcdir}" -', shell
=True, check
=True, encoding
='utf-8',
93 input=srcfile
, capture_output
=True)
95 for line
in result
.stdout
.splitlines():
96 if line
.startswith('DEFVAL '):
97 _
, sym
, val
= line
.split()
98 ret
[sym
.strip('"')] = val
102 def gentvals(output_dir
: Path
,
103 cpp
: str, srctype
: str, srcdir
: Path
,
104 headers
: Iterable
[str],
108 """Extract constants from the specified files using a regular expression.
110 We'll run things through the preprocessor.
112 headers
= tuple(headers
)
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'
119 syms
= extract_syms(cpp
, srcdir
, headers
, pattern
, filter)
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
:
128 if END_MARKER
in line
:
130 assert start_i
and end_i
131 new_lines
= old_lines
[0:start_i
+ 1]
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')
140 def gen_common(output_dir
: Path
, newlib
: Path
, cpp
: str):
141 """Generate the common C library constants.
143 No arch should override these.
145 gentvals(output_dir
, cpp
, 'errno', newlib
/ 'newlib/libc/include',
146 ('errno.h', 'sys/errno.h'), 'E[A-Z0-9]*')
148 gentvals(output_dir
, cpp
, 'signal', newlib
/ 'newlib/libc/include',
149 ('signal.h', 'sys/signal.h'), r
'SIG[A-Z0-9]*', filter=r
'SIGSTKSZ')
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]*')
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
:
163 if END_MARKER
in line
:
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
:]
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
:
175 if END_MARKER
in line
:
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
:]
181 headers
= ('syscall.h',)
182 pattern
= r
'SYS_[_a-zA-Z0-9]*'
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[] = {{')
191 f
'"{sym[4:]}", CB_{sym}, TARGET_NEWLIB_{target.upper()}_{sym}'
193 '#endif' for sym
in sorted(syms
))
194 new_lines_c
.append(' {NULL, -1, -1},')
195 new_lines_c
.append('};\n')
198 f
'extern CB_TARGET_DEFS_MAP cb_{target}_syscall_map[];')
200 f
'#define TARGET_NEWLIB_{target.upper()}_{sym} {val}'
201 for sym
, val
in sorted(syms
.items()))
202 new_lines_h
.append('')
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[] = {{')
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('};')
214 new_lines_h
.append('extern CB_TARGET_DEFS_MAP cb_init_syscall_map[];')
216 f
'#define TARGET_NEWLIB_{sym} {val}'
217 for sym
, val
in sorted(syms
.items()))
219 new_lines_c
.extend(new_lines_c_end
)
220 target_map_c
.write_text('\n'.join(new_lines_c
) + '\n')
222 new_lines_h
.extend(new_lines_h_end
)
223 target_map_h
.write_text('\n'.join(new_lines_h
) + '\n')
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
)
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
)
237 def get_parser() -> argparse
.ArgumentParser
:
238 """Get CLI parser."""
239 parser
= argparse
.ArgumentParser(
241 formatter_class
=argparse
.RawDescriptionHelpFormatter
)
243 '-o', '--output', type=Path
,
244 help='write to the specified directory')
246 '--cpp', type=str, default
='cpp',
247 help='the preprocessor to use')
249 '--srcroot', type=Path
,
250 help='the root of this source tree')
252 'newlib', nargs
='?', type=Path
,
253 help='path to the newlib+libgloss source tree')
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
)
262 if opts
.output
is None:
263 # Default to where the script lives.
264 opts
.output
= Path(__file__
).resolve().parent
266 if opts
.srcroot
is None:
267 opts
.srcroot
= Path(__file__
).resolve().parent
.parent
.parent
269 opts
.srcroot
= opts
.srcroot
.resolve()
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
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')
288 def main(argv
: List
[str]) -> int:
289 """The main entry point for scripts."""
290 opts
= parse_args(argv
)
292 gen(opts
.output
, opts
.newlib
, opts
.cpp
)
296 if __name__
== '__main__':
297 sys
.exit(main(sys
.argv
[1:]))