2 # Copyright (C) 1996-2021 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 nltvals.def.
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.
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 'i960': 'libgloss/i960',
53 'mcore': 'libgloss/mcore',
54 'riscv': 'libgloss/riscv/machine',
55 'sh': 'newlib/libc/sys/sh/sys',
56 'v850': 'libgloss/v850/sys',
60 # The header for the generated def file.
62 /* Newlib/libgloss macro values needed by remote target support. */
63 /* This file is machine generated by {PROG}. */\
67 def gentvals(output
: TextIO
, cpp
: str, srctype
: str, srcdir
: Path
,
68 headers
: Iterable
[str],
71 """Extract constants from the specified files using a regular expression.
73 We'll run things through the preprocessor.
75 headers
= tuple(headers
)
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'
82 if target
is not None:
83 print(f
'#ifdef NL_TARGET_{target}', file=output
)
84 print(f
'#ifdef {srctype}_defs', file=output
)
86 print('\n'.join(f
'/* from {x} */' for x
in headers
), file=output
)
89 print(f
'/* begin {srctype} target macros */', file=output
)
91 print(f
'/* begin {target} {srctype} target macros */', file=output
)
93 # Extract all the symbols.
94 srcfile
= ''.join(f
'#include <{x}>\n' for x
in headers
)
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
:
100 for line
in data
.splitlines():
101 m
= define_pattern
.match(line
)
104 for sym
in sorted(syms
):
105 srcfile
+= f
'#ifdef {sym}\nDEFVAL {{ "{sym}", {sym} }},\n#endif\n'
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
)
114 print(f
'#undef {srctype}_defs', file=output
)
116 print(f
'/* end {srctype} target macros */', file=output
)
118 print(f
'/* end {target} {srctype} target macros */', file=output
)
119 print('#endif', file=output
)
120 print('#endif', file=output
)
123 def gen_common(output
: TextIO
, newlib
: Path
, cpp
: str):
124 """Generate the common C library constants.
126 No arch should override these.
128 gentvals(output
, cpp
, 'errno', newlib
/ 'newlib/libc/include',
129 ('errno.h', 'sys/errno.h'), 'E[A-Z0-9]*')
131 gentvals(output
, cpp
, 'signal', newlib
/ 'newlib/libc/include',
132 ('signal.h', 'sys/signal.h'), r
'SIG[A-Z0-9]*')
134 gentvals(output
, cpp
, 'open', newlib
/ 'newlib/libc/include',
135 ('fcntl.h', 'sys/fcntl.h', 'sys/_default_fcntl.h'), r
'O_[A-Z0-9]*')
138 def gen_targets(output
: TextIO
, newlib
: Path
, cpp
: str):
139 """Generate the target-specific lists."""
140 for target
, subdir
in sorted(TARGET_DIRS
.items()):
141 gentvals(output
, cpp
, 'sys', newlib
/ subdir
, ('syscall.h',),
142 r
'SYS_[_a-zA-Z0-9]*', target
=target
)
144 # Then output the common syscall targets.
145 gentvals(output
, cpp
, 'sys', newlib
/ 'libgloss', ('syscall.h',),
146 r
'SYS_[_a-zA-Z0-9]*')
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
)
156 def get_parser() -> argparse
.ArgumentParser
:
157 """Get CLI parser."""
158 parser
= argparse
.ArgumentParser(
160 formatter_class
=argparse
.RawDescriptionHelpFormatter
)
162 '-o', '--output', type=Path
,
163 help='write to the specified file instead of stdout')
165 '--cpp', type=str, default
='cpp',
166 help='the preprocessor to use')
168 '--srcroot', type=Path
,
169 help='the root of this source tree')
171 'newlib', nargs
='?', type=Path
,
172 help='path to the newlib+libgloss source tree')
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
)
181 if opts
.srcroot
is None:
182 opts
.srcroot
= Path(__file__
).resolve().parent
.parent
.parent
184 if opts
.newlib
is None:
185 # Try to find newlib relative to our source tree.
186 if (opts
.srcroot
/ 'newlib').is_dir():
187 # If newlib is manually in the same source tree, use it.
188 if (opts
.srcroot
/ 'libgloss').is_dir():
189 opts
.newlib
= opts
.srcroot
191 opts
.newlib
= opts
.srcroot
/ 'newlib'
192 elif (opts
.srcroot
.parent
/ 'newlib').is_dir():
193 # Or see if it's alongside the gdb/binutils repo.
194 opts
.newlib
= opts
.srcroot
.parent
/ 'newlib'
195 if opts
.newlib
is None or not opts
.newlib
.is_dir():
196 parser
.error('unable to find newlib')
201 def main(argv
: List
[str]) -> int:
202 """The main entry point for scripts."""
203 opts
= parse_args(argv
)
205 if opts
.output
is not None:
206 output
= open(opts
.output
, 'w', encoding
='utf-8')
210 gen(output
, opts
.newlib
, opts
.cpp
)
214 if __name__
== '__main__':
215 sys
.exit(main(sys
.argv
[1:]))