2 # Copyright (C) 1996-2024 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 # Enable Linux errno extensions since the newlib values are designed to
146 # not conflict with each other.
148 cpp
+ ' -D__LINUX_ERRNO_EXTENSIONS__',
149 'errno', newlib
/ 'newlib/libc/include',
150 ('errno.h', 'sys/errno.h'), 'E[A-Z0-9]*')
152 gentvals(output_dir
, cpp
, 'signal', newlib
/ 'newlib/libc/include',
153 ('signal.h', 'sys/signal.h'), r
'SIG[A-Z0-9]*', filter=r
'SIGSTKSZ')
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]*')
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
:
167 if END_MARKER
in line
:
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
:]
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
:
179 if END_MARKER
in line
:
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
:]
185 headers
= ('syscall.h',)
186 pattern
= r
'SYS_[_a-zA-Z0-9]*'
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[] = {{')
195 f
'"{sym[4:]}", CB_{sym}, TARGET_NEWLIB_{target.upper()}_{sym}'
197 '#endif' for sym
in sorted(syms
))
198 new_lines_c
.append(' {NULL, -1, -1},')
199 new_lines_c
.append('};\n')
202 f
'extern CB_TARGET_DEFS_MAP cb_{target}_syscall_map[];')
204 f
'#define TARGET_NEWLIB_{target.upper()}_{sym} {val}'
205 for sym
, val
in sorted(syms
.items()))
206 new_lines_h
.append('')
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[] = {{')
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('};')
218 new_lines_h
.append('extern CB_TARGET_DEFS_MAP cb_init_syscall_map[];')
220 f
'#define TARGET_NEWLIB_{sym} {val}'
221 for sym
, val
in sorted(syms
.items()))
223 new_lines_c
.extend(new_lines_c_end
)
224 target_map_c
.write_text('\n'.join(new_lines_c
) + '\n')
226 new_lines_h
.extend(new_lines_h_end
)
227 target_map_h
.write_text('\n'.join(new_lines_h
) + '\n')
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
)
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
)
241 def get_parser() -> argparse
.ArgumentParser
:
242 """Get CLI parser."""
243 parser
= argparse
.ArgumentParser(
245 formatter_class
=argparse
.RawDescriptionHelpFormatter
)
247 '-o', '--output', type=Path
,
248 help='write to the specified directory')
250 '--cpp', type=str, default
='cpp',
251 help='the preprocessor to use')
253 '--srcroot', type=Path
,
254 help='the root of this source tree')
256 'newlib', nargs
='?', type=Path
,
257 help='path to the newlib+libgloss source tree')
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
)
266 if opts
.output
is None:
267 # Default to where the script lives.
268 opts
.output
= Path(__file__
).resolve().parent
270 if opts
.srcroot
is None:
271 opts
.srcroot
= Path(__file__
).resolve().parent
.parent
.parent
273 opts
.srcroot
= opts
.srcroot
.resolve()
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
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')
292 def main(argv
: List
[str]) -> int:
293 """The main entry point for scripts."""
294 opts
= parse_args(argv
)
296 gen(opts
.output
, opts
.newlib
, opts
.cpp
)
300 if __name__
== '__main__':
301 sys
.exit(main(sys
.argv
[1:]))