linux-yocto/tools/net/sunrpc/xdrgen/generators/__init__.py
Chuck Lever 4b132aacb0 tools: Add xdrgen
Add a Python-based tool for translating XDR specifications into XDR
encoder and decoder functions written in the Linux kernel's C coding
style. The generator attempts to match the usual C coding style of
the Linux kernel's SunRPC consumers.

This approach is similar to the netlink code generator in
tools/net/ynl .

The maintainability benefits of machine-generated XDR code include:

- Stronger type checking
- Reduces the number of bugs introduced by human error
- Makes the XDR code easier to audit and analyze
- Enables rapid prototyping of new RPC-based protocols
- Hardens the layering between protocol logic and marshaling
- Makes it easier to add observability on demand
- Unit tests might be built for both the tool and (automatically)
  for the generated code

In addition, converting the XDR layer to use memory-safe languages
such as Rust will be easier if much of the code can be converted
automatically.

Tested-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2024-09-20 19:31:39 -04:00

114 lines
4.2 KiB
Python

# SPDX-License-Identifier: GPL-2.0
"""Define a base code generator class"""
import sys
from jinja2 import Environment, FileSystemLoader, Template
from xdr_ast import _XdrAst, Specification, _RpcProgram, _XdrTypeSpecifier
from xdr_ast import public_apis, pass_by_reference, get_header_name
from xdr_parse import get_xdr_annotate
def create_jinja2_environment(language: str, xdr_type: str) -> Environment:
"""Open a set of templates based on output language"""
match language:
case "C":
environment = Environment(
loader=FileSystemLoader(sys.path[0] + "/templates/C/" + xdr_type + "/"),
trim_blocks=True,
lstrip_blocks=True,
)
environment.globals["annotate"] = get_xdr_annotate()
environment.globals["public_apis"] = public_apis
environment.globals["pass_by_reference"] = pass_by_reference
return environment
case _:
raise NotImplementedError("Language not supported")
def get_jinja2_template(
environment: Environment, template_type: str, template_name: str
) -> Template:
"""Retrieve a Jinja2 template for emitting source code"""
return environment.get_template(template_type + "/" + template_name + ".j2")
def find_xdr_program_name(root: Specification) -> str:
"""Retrieve the RPC program name from an abstract syntax tree"""
raw_name = get_header_name()
if raw_name != "none":
return raw_name.lower()
for definition in root.definitions:
if isinstance(definition.value, _RpcProgram):
raw_name = definition.value.name
return raw_name.lower().removesuffix("_program").removesuffix("_prog")
return "noprog"
def header_guard_infix(filename: str) -> str:
"""Extract the header guard infix from the specification filename"""
basename = filename.split("/")[-1]
program = basename.replace(".x", "")
return program.upper()
def kernel_c_type(spec: _XdrTypeSpecifier) -> str:
"""Return name of C type"""
builtin_native_c_type = {
"bool": "bool",
"int": "s32",
"unsigned_int": "u32",
"long": "s32",
"unsigned_long": "u32",
"hyper": "s64",
"unsigned_hyper": "u64",
}
if spec.type_name in builtin_native_c_type:
return builtin_native_c_type[spec.type_name]
return spec.type_name
class Boilerplate:
"""Base class to generate boilerplate for source files"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
raise NotImplementedError("No language support defined")
def emit_declaration(self, filename: str, root: Specification) -> None:
"""Emit declaration header boilerplate"""
raise NotImplementedError("Header boilerplate generation not supported")
def emit_definition(self, filename: str, root: Specification) -> None:
"""Emit definition header boilerplate"""
raise NotImplementedError("Header boilerplate generation not supported")
def emit_source(self, filename: str, root: Specification) -> None:
"""Emit generic source code for this XDR type"""
raise NotImplementedError("Source boilerplate generation not supported")
class SourceGenerator:
"""Base class to generate header and source code for XDR types"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
raise NotImplementedError("No language support defined")
def emit_declaration(self, node: _XdrAst) -> None:
"""Emit one function declaration for this XDR type"""
raise NotImplementedError("Declaration generation not supported")
def emit_decoder(self, node: _XdrAst) -> None:
"""Emit one decoder function for this XDR type"""
raise NotImplementedError("Decoder generation not supported")
def emit_definition(self, node: _XdrAst) -> None:
"""Emit one definition for this XDR type"""
raise NotImplementedError("Definition generation not supported")
def emit_encoder(self, node: _XdrAst) -> None:
"""Emit one encoder function for this XDR type"""
raise NotImplementedError("Encoder generation not supported")