xref: /linux/tools/net/sunrpc/xdrgen/subcmds/source.py (revision 4c53b89032f14577e94d747a3ca0aee63f18d856)
1#!/usr/bin/env python3
2# ex: set filetype=python:
3
4"""Translate an XDR specification into executable code that
5can be compiled for the Linux kernel."""
6
7import logging
8
9from argparse import Namespace
10from lark import logger
11from lark.exceptions import VisitError
12
13from generators.source_top import XdrSourceTopGenerator
14from generators.enum import XdrEnumGenerator
15from generators.pointer import XdrPointerGenerator
16from generators.program import XdrProgramGenerator
17from generators.typedef import XdrTypedefGenerator
18from generators.struct import XdrStructGenerator
19from generators.union import XdrUnionGenerator
20
21from xdr_ast import transform_parse_tree, _RpcProgram, Specification
22from xdr_ast import _XdrAst, _XdrEnum, _XdrPointer
23from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion
24
25from xdr_parse import xdr_parser, set_xdr_annotate
26from xdr_parse import make_error_handler, XdrParseError
27from xdr_parse import handle_transform_error
28
29logger.setLevel(logging.INFO)
30
31
32def emit_source_decoder(node: _XdrAst, language: str, peer: str) -> None:
33    """Emit one XDR decoder function for a source file"""
34    if isinstance(node, _XdrEnum):
35        gen = XdrEnumGenerator(language, peer)
36    elif isinstance(node, _XdrPointer):
37        gen = XdrPointerGenerator(language, peer)
38    elif isinstance(node, _XdrTypedef):
39        gen = XdrTypedefGenerator(language, peer)
40    elif isinstance(node, _XdrStruct):
41        gen = XdrStructGenerator(language, peer)
42    elif isinstance(node, _XdrUnion):
43        gen = XdrUnionGenerator(language, peer)
44    elif isinstance(node, _RpcProgram):
45        gen = XdrProgramGenerator(language, peer)
46    else:
47        return
48    gen.emit_decoder(node)
49
50
51def emit_source_encoder(node: _XdrAst, language: str, peer: str) -> None:
52    """Emit one XDR encoder function for a source file"""
53    if isinstance(node, _XdrEnum):
54        gen = XdrEnumGenerator(language, peer)
55    elif isinstance(node, _XdrPointer):
56        gen = XdrPointerGenerator(language, peer)
57    elif isinstance(node, _XdrTypedef):
58        gen = XdrTypedefGenerator(language, peer)
59    elif isinstance(node, _XdrStruct):
60        gen = XdrStructGenerator(language, peer)
61    elif isinstance(node, _XdrUnion):
62        gen = XdrUnionGenerator(language, peer)
63    elif isinstance(node, _RpcProgram):
64        gen = XdrProgramGenerator(language, peer)
65    else:
66        return
67    gen.emit_encoder(node)
68
69
70def generate_server_source(filename: str, root: Specification, language: str) -> None:
71    """Generate server-side source code"""
72
73    gen = XdrSourceTopGenerator(language, "server")
74    gen.emit_source(filename, root)
75
76    for definition in root.definitions:
77        emit_source_decoder(definition.value, language, "server")
78    for definition in root.definitions:
79        emit_source_encoder(definition.value, language, "server")
80
81
82def generate_client_source(filename: str, root: Specification, language: str) -> None:
83    """Generate server-side source code"""
84
85    gen = XdrSourceTopGenerator(language, "client")
86    gen.emit_source(filename, root)
87
88    print("")
89    for definition in root.definitions:
90        emit_source_encoder(definition.value, language, "client")
91    for definition in root.definitions:
92        emit_source_decoder(definition.value, language, "client")
93
94    # cel: todo: client needs PROC macros
95
96
97def subcmd(args: Namespace) -> int:
98    """Generate encoder and decoder functions"""
99
100    set_xdr_annotate(args.annotate)
101    parser = xdr_parser()
102    with open(args.filename, encoding="utf-8") as f:
103        source = f.read()
104        try:
105            parse_tree = parser.parse(
106                source, on_error=make_error_handler(source, args.filename)
107            )
108        except XdrParseError:
109            return 1
110        try:
111            ast = transform_parse_tree(parse_tree)
112        except VisitError as e:
113            handle_transform_error(e, source, args.filename)
114            return 1
115        match args.peer:
116            case "server":
117                generate_server_source(args.filename, ast, args.language)
118            case "client":
119                generate_client_source(args.filename, ast, args.language)
120            case _:
121                print("Code generation for", args.peer, "is not yet supported")
122
123    return 0
124