xref: /linux/tools/net/sunrpc/xdrgen/subcmds/source.py (revision 69050f8d6d075dc01af7a5f2f550a8067510366f)
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.passthru import XdrPassthruGenerator
16from generators.pointer import XdrPointerGenerator
17from generators.program import XdrProgramGenerator
18from generators.typedef import XdrTypedefGenerator
19from generators.struct import XdrStructGenerator
20from generators.union import XdrUnionGenerator
21
22from xdr_ast import transform_parse_tree, _RpcProgram, Specification
23from xdr_ast import _XdrAst, _XdrEnum, _XdrPassthru, _XdrPointer
24from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion
25
26from xdr_parse import xdr_parser, set_xdr_annotate, set_xdr_enum_validation
27from xdr_parse import make_error_handler, XdrParseError
28from xdr_parse import handle_transform_error
29
30logger.setLevel(logging.INFO)
31
32
33def emit_source_decoder(node: _XdrAst, language: str, peer: str) -> None:
34    """Emit one XDR decoder function for a source file"""
35    if isinstance(node, _XdrEnum):
36        gen = XdrEnumGenerator(language, peer)
37    elif isinstance(node, _XdrPointer):
38        gen = XdrPointerGenerator(language, peer)
39    elif isinstance(node, _XdrTypedef):
40        gen = XdrTypedefGenerator(language, peer)
41    elif isinstance(node, _XdrStruct):
42        gen = XdrStructGenerator(language, peer)
43    elif isinstance(node, _XdrUnion):
44        gen = XdrUnionGenerator(language, peer)
45    elif isinstance(node, _RpcProgram):
46        gen = XdrProgramGenerator(language, peer)
47    else:
48        return
49    gen.emit_decoder(node)
50
51
52def emit_source_encoder(node: _XdrAst, language: str, peer: str) -> None:
53    """Emit one XDR encoder function for a source file"""
54    if isinstance(node, _XdrEnum):
55        gen = XdrEnumGenerator(language, peer)
56    elif isinstance(node, _XdrPointer):
57        gen = XdrPointerGenerator(language, peer)
58    elif isinstance(node, _XdrTypedef):
59        gen = XdrTypedefGenerator(language, peer)
60    elif isinstance(node, _XdrStruct):
61        gen = XdrStructGenerator(language, peer)
62    elif isinstance(node, _XdrUnion):
63        gen = XdrUnionGenerator(language, peer)
64    elif isinstance(node, _RpcProgram):
65        gen = XdrProgramGenerator(language, peer)
66    else:
67        return
68    gen.emit_encoder(node)
69
70
71def generate_server_source(filename: str, root: Specification, language: str) -> None:
72    """Generate server-side source code"""
73
74    gen = XdrSourceTopGenerator(language, "server")
75    gen.emit_source(filename, root)
76
77    for definition in root.definitions:
78        if isinstance(definition.value, _XdrPassthru):
79            passthru_gen = XdrPassthruGenerator(language, "server")
80            passthru_gen.emit_decoder(definition.value)
81        else:
82            emit_source_decoder(definition.value, language, "server")
83    for definition in root.definitions:
84        if not isinstance(definition.value, _XdrPassthru):
85            emit_source_encoder(definition.value, language, "server")
86
87
88def generate_client_source(filename: str, root: Specification, language: str) -> None:
89    """Generate client-side source code"""
90
91    gen = XdrSourceTopGenerator(language, "client")
92    gen.emit_source(filename, root)
93
94    for definition in root.definitions:
95        if isinstance(definition.value, _XdrPassthru):
96            passthru_gen = XdrPassthruGenerator(language, "client")
97            passthru_gen.emit_decoder(definition.value)
98        else:
99            emit_source_encoder(definition.value, language, "client")
100    for definition in root.definitions:
101        if not isinstance(definition.value, _XdrPassthru):
102            emit_source_decoder(definition.value, language, "client")
103
104    # cel: todo: client needs PROC macros
105
106
107def subcmd(args: Namespace) -> int:
108    """Generate encoder and decoder functions"""
109
110    set_xdr_annotate(args.annotate)
111    set_xdr_enum_validation(not args.no_enum_validation)
112    parser = xdr_parser()
113    with open(args.filename, encoding="utf-8") as f:
114        source = f.read()
115        try:
116            parse_tree = parser.parse(
117                source, on_error=make_error_handler(source, args.filename)
118            )
119        except XdrParseError:
120            return 1
121        try:
122            ast = transform_parse_tree(parse_tree)
123        except VisitError as e:
124            handle_transform_error(e, source, args.filename)
125            return 1
126        match args.peer:
127            case "server":
128                generate_server_source(args.filename, ast, args.language)
129            case "client":
130                generate_client_source(args.filename, ast, args.language)
131            case _:
132                print("Code generation for", args.peer, "is not yet supported")
133
134    return 0
135