binutils-gdb/gdb/make-target-delegates.py
Andrew Burgess 1d506c26d9 Update copyright year range in header of all files managed by GDB
This commit is the result of the following actions:

  - Running gdb/copyright.py to update all of the copyright headers to
    include 2024,

  - Manually updating a few files the copyright.py script told me to
    update, these files had copyright headers embedded within the
    file,

  - Regenerating gdbsupport/Makefile.in to refresh it's copyright
    date,

  - Using grep to find other files that still mentioned 2023.  If
    these files were updated last year from 2022 to 2023 then I've
    updated them this year to 2024.

I'm sure I've probably missed some dates.  Feel free to fix them up as
you spot them.
2024-01-12 15:49:57 +00:00

389 lines
12 KiB
Python
Executable File

#!/usr/bin/env python3
# Copyright (C) 2013-2024 Free Software Foundation, Inc.
#
# This file is part of GDB.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Usage:
# make-target-delegates.py
import re
from typing import Dict, List, TextIO
import gdbcopyright
# The line we search for in target.h that marks where we should start
# looking for methods.
TRIGGER = re.compile(r"^struct target_ops$")
# The end of the methods part.
ENDER = re.compile(r"^\s*};$")
# Match a C symbol.
SYMBOL = "[a-zA-Z_][a-zA-Z0-9_]*"
# Match the name part of a method in struct target_ops.
NAME_PART = r"(?P<name>" + SYMBOL + r")\s"
# Match the arguments to a method.
ARGS_PART = r"(?P<args>\(.*\))"
# We strip the indentation so here we only need the caret.
INTRO_PART = r"^"
POINTER_PART = r"\s*(\*|\&)?\s*"
# Match a C++ symbol, including scope operators and template
# parameters. E.g., 'std::vector<something>'.
CP_SYMBOL = r"[a-zA-Z_][a-zA-Z0-9_<>:]*"
# Match the return type when it is "ordinary".
SIMPLE_RETURN_PART = r"((struct|class|enum|union)\s+)?" + CP_SYMBOL
# Match a return type.
RETURN_PART = r"((const|volatile)\s+)?(" + SIMPLE_RETURN_PART + ")" + POINTER_PART
# Match "virtual".
VIRTUAL_PART = r"virtual\s"
# Match the TARGET_DEFAULT_* attribute for a method.
TARGET_DEFAULT_PART = r"TARGET_DEFAULT_(?P<style>[A-Z_]+)\s*\((?P<default_arg>.*)\)"
# Match the arguments and trailing attribute of a method definition.
# Note we don't match the trailing ";".
METHOD_TRAILER = r"\s*" + TARGET_DEFAULT_PART + "$"
# Match an entire method definition.
METHOD = re.compile(
INTRO_PART
+ VIRTUAL_PART
+ "(?P<return_type>"
+ RETURN_PART
+ ")"
+ NAME_PART
+ ARGS_PART
+ METHOD_TRAILER
)
# Space-separated symbols.
CP_SYMBOLS = CP_SYMBOL + r"(\s+" + CP_SYMBOL + r")*"
# Regular expression used to dissect argument types.
ARGTYPES = re.compile(
"^("
+ r"(?P<E>enum\s+"
+ SYMBOL
+ r")"
+ r"|(?P<T>"
+ CP_SYMBOLS
+ r"(\s|\*|&)+)"
+ SYMBOL
+ ")$"
)
# Match TARGET_DEBUG_PRINTER in an argument type.
# This must match the whole "sub-expression" including the parens.
TARGET_DEBUG_PRINTER = r"\s*TARGET_DEBUG_PRINTER\s*\((?P<arg>[^)]*)\)\s*"
class Entry:
def __init__(
self, argtypes: List[str], return_type: str, style: str, default_arg: str
):
self.argtypes = argtypes
self.return_type = return_type
self.style = style
self.default_arg = default_arg
def scan_target_h():
found_trigger = False
all_the_text = ""
with open("target.h", "r") as target_h:
for line in target_h:
line = line.strip()
if not found_trigger:
if TRIGGER.match(line):
found_trigger = True
elif "{" in line:
# Skip the open brace.
pass
elif ENDER.match(line):
break
else:
# Strip // comments.
line = re.split("//", line)[0]
all_the_text = all_the_text + " " + line
if not found_trigger:
raise RuntimeError("Could not find trigger line")
# Now strip out the C comments.
all_the_text = re.sub(r"/\*(.*?)\*/", "", all_the_text)
# Replace sequences whitespace with a single space character.
# We need the space because the method may have been split
# between multiple lines, like e.g.:
#
# virtual std::vector<long_type_name>
# my_long_method_name ()
# TARGET_DEFAULT_IGNORE ();
#
# If we didn't preserve the space, then we'd end up with:
#
# virtual std::vector<long_type_name>my_long_method_name ()TARGET_DEFAULT_IGNORE ()
#
# ... which wouldn't later be parsed correctly.
all_the_text = re.sub(r"\s+", " ", all_the_text)
return all_the_text.split(";")
# Parse arguments into a list.
def parse_argtypes(typestr: str):
# Remove the outer parens.
typestr = re.sub(r"^\((.*)\)$", r"\1", typestr)
result: list[str] = []
for item in re.split(r",\s*", typestr):
if item == "void" or item == "":
continue
m = ARGTYPES.match(item)
if m:
if m.group("E"):
onetype = m.group("E")
else:
onetype = m.group("T")
else:
onetype = item
result.append(onetype.strip())
return result
# Write function header given name, return type, and argtypes.
# Returns a list of actual argument names.
def write_function_header(
f: TextIO, decl: bool, name: str, return_type: str, argtypes: List[str]
):
print(return_type, file=f, end="")
if decl:
if not return_type.endswith("*"):
print(" ", file=f, end="")
else:
print("", file=f)
print(name + " (", file=f, end="")
argdecls: list[str] = []
actuals: list[str] = []
for i in range(len(argtypes)):
val = re.sub(TARGET_DEBUG_PRINTER, "", argtypes[i])
if not val.endswith("*") and not val.endswith("&"):
val = val + " "
vname = "arg" + str(i)
val = val + vname
argdecls.append(val)
actuals.append(vname)
print(", ".join(argdecls) + ")", file=f, end="")
if decl:
print(" override;", file=f)
else:
print("\n{", file=f)
return actuals
# Write out a declaration.
def write_declaration(f: TextIO, name: str, return_type: str, argtypes: List[str]):
write_function_header(f, True, name, return_type, argtypes)
# Write out a delegation function.
def write_delegator(f: TextIO, name: str, return_type: str, argtypes: List[str]):
print("", file=f)
names = write_function_header(
f, False, "target_ops::" + name, return_type, argtypes
)
print(" ", file=f, end="")
if return_type != "void":
print("return ", file=f, end="")
print("this->beneath ()->" + name + " (", file=f, end="")
print(", ".join(names), file=f, end="")
print(");", file=f)
print("}", file=f)
# Write out a default function.
def write_tdefault(
f: TextIO,
content: str,
style: str,
name: str,
return_type: str,
argtypes: List[str],
):
print("", file=f)
name = "dummy_target::" + name
names = write_function_header(f, False, name, return_type, argtypes)
if style == "FUNC":
print(" ", file=f, end="")
if return_type != "void":
print("return ", file=f, end="")
print(content + " (", file=f, end="")
names.insert(0, "this")
print(", ".join(names) + ");", file=f)
elif style == "RETURN":
print(" return " + content + ";", file=f)
elif style == "NORETURN":
print(" " + content + ";", file=f)
elif style == "IGNORE":
# Nothing.
pass
else:
raise RuntimeError("unrecognized style: " + style)
print("}", file=f)
def munge_type(typename: str):
m = re.search(TARGET_DEBUG_PRINTER, typename)
if m:
return m.group("arg")
typename = typename.rstrip()
# There's no reason to have these keywords in the name, and their
# presence makes it harder to change styles.
typename = re.sub("\\b(struct|enum|class|union) ", "", typename)
typename = re.sub("[ ()<>:]", "_", typename)
typename = re.sub("[*]", "p", typename)
typename = re.sub("&", "r", typename)
# Identifiers with double underscores are reserved to the C++
# implementation.
typename = re.sub("_+", "_", typename)
# Avoid ending the function name with underscore, for
# cosmetics. Trailing underscores appear after munging types
# with template parameters, like e.g. "foo<int>".
typename = re.sub("_+$", "", typename)
return "target_debug_print_" + typename
# Write out a debug method.
def write_debugmethod(
f: TextIO, content: str, name: str, return_type: str, argtypes: List[str]
):
print("", file=f)
debugname = "debug_target::" + name
names = write_function_header(f, False, debugname, return_type, argtypes)
print(
' gdb_printf (gdb_stdlog, "-> %s->'
+ name
+ ' (...)\\n", this->beneath ()->shortname ());',
file=f,
)
# Delegate to the beneath target.
if return_type != "void":
print(" " + return_type + " result", file=f)
print(" = ", file=f, end="")
else:
print(" ", file=f, end="")
print("this->beneath ()->" + name + " (", file=f, end="")
print(", ".join(names), file=f, end="")
print(");", file=f)
# Now print the arguments.
print(
' gdb_printf (gdb_stdlog, "<- %s->'
+ name
+ ' (", this->beneath ()->shortname ());',
file=f,
)
for i in range(len(argtypes)):
if i > 0:
print(' gdb_puts (", ", gdb_stdlog);', file=f)
printer = munge_type(argtypes[i])
print(" " + printer + " (" + names[i] + ");", file=f)
if return_type != "void":
print(' gdb_puts (") = ", gdb_stdlog);', file=f)
printer = munge_type(return_type)
print(" " + printer + " (result);", file=f)
print(' gdb_puts ("\\n", gdb_stdlog);', file=f)
else:
print(' gdb_puts (")\\n", gdb_stdlog);', file=f)
if return_type != "void":
print(" return result;", file=f)
print("}", file=f)
def print_class(
f: TextIO,
class_name: str,
delegators: List[str],
entries: Dict[str, Entry],
):
print("", file=f)
print("struct " + class_name + " : public target_ops", file=f)
print("{", file=f)
print(" const target_info &info () const override;", file=f)
print("", file=f)
print(" strata stratum () const override;", file=f)
print("", file=f)
for name in delegators:
print(" ", file=f, end="")
entry = entries[name]
write_declaration(f, name, entry.return_type, entry.argtypes)
print("};", file=f)
delegators: List[str] = []
entries: Dict[str, Entry] = {}
for current_line in scan_target_h():
# See comments in scan_target_h. Here we strip away the leading
# and trailing whitespace.
current_line = current_line.strip()
m = METHOD.match(current_line)
if not m:
continue
data = m.groupdict()
name = data["name"]
argtypes = parse_argtypes(data["args"])
return_type = data["return_type"].strip()
style = data["style"]
default_arg = data["default_arg"]
entries[name] = Entry(argtypes, return_type, style, default_arg)
delegators.append(name)
with open("target-delegates.c", "w") as f:
print(
gdbcopyright.copyright(
"make-target-delegates.py", "Boilerplate target methods for GDB"
),
file=f,
)
print_class(f, "dummy_target", delegators, entries)
print_class(f, "debug_target", delegators, entries)
for name in delegators:
entry = entries[name]
write_delegator(f, name, entry.return_type, entry.argtypes)
write_tdefault(
f,
entry.default_arg,
entry.style,
name,
entry.return_type,
entry.argtypes,
)
write_debugmethod(
f,
entry.default_arg,
name,
entry.return_type,
entry.argtypes,
)