1# Copyright (C) 2023 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15load("@bazel_skylib//lib:paths.bzl", "paths")
16load(":cc_library_common.bzl", "create_ccinfo_for_includes")
17load(":cc_library_static.bzl", "cc_library_static")
18
19"""
20Generate C/C++ parser from a .y/.yy grammar file.
21The generated file might depend on external headers and has to be compiled
22separately.
23"""
24
25def _cc_yacc_parser_gen_impl(ctx):
26    ext = ".c"
27    if paths.split_extension(ctx.file.src.basename)[1] == ".yy":
28        ext = ".cpp"
29
30    # C/CPP file
31    output_c_file = ctx.actions.declare_file(
32        paths.join(
33            ctx.attr.name,  # Prevent name collisions (esp for tests)
34            paths.replace_extension(ctx.file.src.basename, ext),
35        ),
36    )
37
38    # Header file
39    output_h_file = ctx.actions.declare_file(
40        paths.join(
41            ctx.attr.name,  # Prevent name collisions (esp for tests)
42            paths.replace_extension(ctx.file.src.basename, ".h"),
43        ),
44    )
45    outputs = [
46        output_c_file,
47        output_h_file,
48    ]
49    output_hdrs = [
50        output_h_file,
51    ]
52
53    # Path of location.hh in the same dir as the generated C/CPP file
54    if ctx.attr.gen_location_hh:
55        location_hh_file = ctx.actions.declare_file(
56            paths.join(
57                ctx.attr.name,
58                "location.hh",
59            ),
60        )
61        outputs.append(location_hh_file)
62        output_hdrs.append(location_hh_file)
63
64    # Path of position.hh in the same dir as the generated C/CPP file
65    if ctx.attr.gen_position_hh:
66        position_hh_file = ctx.actions.declare_file(
67            paths.join(
68                ctx.attr.name,
69                "position.hh",
70            ),
71        )
72        outputs.append(position_hh_file)
73        output_hdrs.append(position_hh_file)
74
75    args = ctx.actions.args()
76    args.add("-d")  # Generate headers
77    args.add_all(ctx.attr.flags)
78    args.add("--defines=" + output_h_file.path)
79    args.add("-o", output_c_file)
80    args.add(ctx.file.src)
81
82    ctx.actions.run(
83        executable = ctx.executable._bison,
84        inputs = [ctx.file.src],
85        outputs = outputs,
86        arguments = [args],
87        # Explicitly set some environment variables to ensure Android's hermetic tools are used.
88        env = {
89            "BISON_PKGDATADIR": "prebuilts/build-tools/common/bison",
90            "M4": ctx.executable._m4.path,
91        },
92        tools = [ctx.executable._m4] + ctx.files._bison_runfiles,
93        mnemonic = "YaccCompile",
94    )
95
96    return [
97        DefaultInfo(
98            # Return the C/C++ file.
99            # Skip headers since rdep does not compile headers.
100            files = depset([output_c_file]),
101        ),
102        create_ccinfo_for_includes(
103            ctx,
104            hdrs = output_hdrs,
105            includes = [ctx.attr.name],
106        ),
107    ]
108
109_cc_yacc_parser_gen = rule(
110    implementation = _cc_yacc_parser_gen_impl,
111    doc = "This rule generates a C/C++ parser from a .y/.yy grammar file using bison",
112    attrs = {
113        "src": attr.label(
114            allow_single_file = [".y", ".yy"],
115            doc = "The grammar file for the parser",
116        ),
117        "flags": attr.string_list(
118            default = [],
119            doc = "List of flags that will be used in yacc compile",
120        ),
121        "gen_location_hh": attr.bool(
122            default = False,
123            doc = "Whether the yacc file will produce a location.hh file.",
124        ),
125        "gen_position_hh": attr.bool(
126            default = False,
127            doc = "Whether the yacc file will produce a location.hh file.",
128        ),
129        "_m4": attr.label(
130            executable = True,
131            cfg = "exec",
132            default = Label("//prebuilts/build-tools:m4"),
133        ),
134        "_bison": attr.label(
135            executable = True,
136            cfg = "exec",
137            default = Label("//prebuilts/build-tools:bison"),
138        ),
139        "_bison_runfiles": attr.label(
140            default = Label("//prebuilts/build-tools:bison.runfiles"),
141        ),
142    },
143    provides = [
144        CcInfo,
145    ],
146)
147
148def cc_yacc_static_library(
149        name,
150        src,
151        flags = [],
152        gen_location_hh = False,
153        gen_position_hh = False,
154        local_includes = [],
155        implementation_deps = [],
156        implementation_dynamic_deps = [],
157        **kwargs):
158    """
159    Generate C/C++ parser from .y/.yy grammar file and wrap it in a cc_library_static target.
160
161    """
162    _output_parser = name + "_parser"
163
164    _cc_yacc_parser_gen(
165        name = _output_parser,
166        src = src,
167        flags = flags,
168        gen_location_hh = gen_location_hh,
169        gen_position_hh = gen_position_hh,
170        **kwargs
171    )
172
173    cc_library_static(
174        name = name,
175        srcs = [_output_parser],
176        deps = [_output_parser],  # Generated hdrs
177        local_includes = local_includes,
178        implementation_deps = implementation_deps,
179        implementation_dynamic_deps = implementation_dynamic_deps,
180        **kwargs
181    )
182