1"""Build rule for generating C or C++ sources with Bison.
2
3Notes about compiling libraries using bison: Make sure to use
4
5  cc_library(
6    ...
7    copts = [
8        "-Wno-implicit-fallthrough",
9        "-O2",
10        "-Wno-sign-compare",
11    ],
12
13The Bison skeleton uses implicit fallthrough, so libraries won't compile unless
14we turn the 'no-implicit-fallthrough' warning off. The -O2 is required because
15the generated bison parser uses a ton of stack space in non-optimized builds.
16The entire parser is contained in a single giant function, and non-optimized
17compilation does not reuse stack slots for local variables. Without -O2, clients
18are likely to fail in fastbuild with default 64KB stack sizes, or to start
19failing when the parser is extended further.
20
21"""
22
23load("@rules_m4//m4:m4.bzl", "M4_TOOLCHAIN_TYPE", "m4_toolchain")
24
25def _genyacc_impl(ctx):
26    """Implementation for genyacc rule."""
27
28    # Argument list
29    args = ctx.actions.args()
30    args.add("--defines=%s" % ctx.outputs.header_out.path)
31    if ctx.outputs.location_out:
32        args.add('--define=api.location.file="%s"' % ctx.outputs.location_out.basename)
33        args.add('--define=api.location.include=""%s""' % ctx.outputs.location_out.short_path)
34    args.add("--output-file=%s" % ctx.outputs.source_out.path)
35    if ctx.attr.prefix:
36        args.add("--name-prefix=%s" % ctx.attr.prefix)
37    args.add_all([ctx.expand_location(opt) for opt in ctx.attr.extra_options])
38    args.add(ctx.file.src.path)
39
40    # Output files
41    outputs = ctx.outputs.extra_outs + [
42        ctx.outputs.header_out,
43        ctx.outputs.location_out,
44        ctx.outputs.source_out,
45    ]
46
47    m4 = m4_toolchain(ctx)
48    ctx.actions.run(
49        executable = ctx.executable._bison,
50        env = {
51            "M4": m4.m4_tool.executable.path,
52        },
53        arguments = [args],
54        inputs = ctx.files.src,
55        tools = [m4.m4_tool.executable],
56        outputs = outputs,
57        mnemonic = "Yacc",
58        progress_message = "Generating %s and %s from %s" %
59                           (
60                               ctx.outputs.source_out.short_path,
61                               ctx.outputs.header_out.short_path,
62                               ctx.file.src.short_path,
63                           ),
64    )
65
66genyacc = rule(
67    implementation = _genyacc_impl,
68    doc = "Generate C/C++-language sources from a Yacc file using Bison.",
69    attrs = {
70        "src": attr.label(
71            mandatory = True,
72            allow_single_file = [".y", ".yy", ".yc", ".ypp", ".yxx"],
73            doc = "The .y, .yy, or .yc source file for this rule",
74        ),
75        "header_out": attr.output(
76            mandatory = True,
77            doc = "The generated 'defines' header file",
78        ),
79        "location_out": attr.output(
80            mandatory = False,
81            doc = "The generated 'location' header file",
82        ),
83        "source_out": attr.output(mandatory = True, doc = "The generated source file"),
84        "prefix": attr.string(
85            doc = "External symbol prefix for Bison. This string is " +
86                  "passed to bison as the -p option, causing the resulting C " +
87                  "file to define external functions named 'prefix'parse, " +
88                  "'prefix'lex, etc. instead of yyparse, yylex, etc.",
89        ),
90        "extra_outs": attr.output_list(doc = "A list of extra generated output files."),
91        "extra_options": attr.string_list(
92            doc = "A list of extra options to pass to Bison.  These are " +
93                  "subject to $(location ...) expansion.",
94        ),
95        "_bison": attr.label(
96            default = Label("//third_party/parser:bison_bin"),
97            executable = True,
98            cfg = "host",
99        ),
100    },
101    output_to_genfiles = True,
102    toolchains = [M4_TOOLCHAIN_TYPE],
103)
104