xref: /aosp_15_r20/external/wayland-protocols/bazel/gensrcs.bzl (revision 6c119a463dd5c45dd05bbe67429293292dde15ee)
1*6c119a46SAndroid Build Coastguard Worker"""
2*6c119a46SAndroid Build Coastguard WorkerCopyright 2023 The Android Open Source Project
3*6c119a46SAndroid Build Coastguard Worker
4*6c119a46SAndroid Build Coastguard WorkerLicensed under the Apache License, Version 2.0 (the "License");
5*6c119a46SAndroid Build Coastguard Workeryou may not use this file except in compliance with the License.
6*6c119a46SAndroid Build Coastguard WorkerYou may obtain a copy of the License at
7*6c119a46SAndroid Build Coastguard Worker
8*6c119a46SAndroid Build Coastguard Worker    http://www.apache.org/licenses/LICENSE-2.0
9*6c119a46SAndroid Build Coastguard Worker
10*6c119a46SAndroid Build Coastguard WorkerUnless required by applicable law or agreed to in writing, software
11*6c119a46SAndroid Build Coastguard Workerdistributed under the License is distributed on an "AS IS" BASIS,
12*6c119a46SAndroid Build Coastguard WorkerWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6c119a46SAndroid Build Coastguard WorkerSee the License for the specific language governing permissions and
14*6c119a46SAndroid Build Coastguard Workerlimitations under the License.
15*6c119a46SAndroid Build Coastguard Worker"""
16*6c119a46SAndroid Build Coastguard Worker
17*6c119a46SAndroid Build Coastguard Workerload("@bazel_skylib//lib:paths.bzl", "paths")
18*6c119a46SAndroid Build Coastguard Worker
19*6c119a46SAndroid Build Coastguard Workerdef _remove_extension(p):
20*6c119a46SAndroid Build Coastguard Worker    """Removes the extension from the path `p`.
21*6c119a46SAndroid Build Coastguard Worker
22*6c119a46SAndroid Build Coastguard Worker    Leading periods on the basename are ignored, so
23*6c119a46SAndroid Build Coastguard Worker    `_strip_extension(".bashrc")` returns `".bashrc"`.
24*6c119a46SAndroid Build Coastguard Worker
25*6c119a46SAndroid Build Coastguard Worker    Args:
26*6c119a46SAndroid Build Coastguard Worker      p: The path to modify.
27*6c119a46SAndroid Build Coastguard Worker
28*6c119a46SAndroid Build Coastguard Worker    Returns:
29*6c119a46SAndroid Build Coastguard Worker      The path with the extension removed.
30*6c119a46SAndroid Build Coastguard Worker    """
31*6c119a46SAndroid Build Coastguard Worker
32*6c119a46SAndroid Build Coastguard Worker    # paths.split_extension() does all of the work.
33*6c119a46SAndroid Build Coastguard Worker    return paths.split_extension(p)[0]
34*6c119a46SAndroid Build Coastguard Worker
35*6c119a46SAndroid Build Coastguard Worker# Expands an output path template for the given context and input file.
36*6c119a46SAndroid Build Coastguard Workerdef _expand_out_path_template(ctx, src_file):
37*6c119a46SAndroid Build Coastguard Worker    # Each src_file has a short_path that looks like:
38*6c119a46SAndroid Build Coastguard Worker    #
39*6c119a46SAndroid Build Coastguard Worker    #   <source-package-path>/<source-package-rel-path>/<base.ext>
40*6c119a46SAndroid Build Coastguard Worker    #
41*6c119a46SAndroid Build Coastguard Worker    # For some expansions, we want to strip of the source package path, and
42*6c119a46SAndroid Build Coastguard Worker    # only use the rest for output file path in the expansion.
43*6c119a46SAndroid Build Coastguard Worker    #
44*6c119a46SAndroid Build Coastguard Worker    # There is also an option during expansion to just use the <base> or
45*6c119a46SAndroid Build Coastguard Worker    # <base.ext> portion of the input path.
46*6c119a46SAndroid Build Coastguard Worker    #
47*6c119a46SAndroid Build Coastguard Worker    # This means there can be collisions if input files are taken from
48*6c119a46SAndroid Build Coastguard Worker    # `filegroups` defined in different packages, if they happen to use
49*6c119a46SAndroid Build Coastguard Worker    # the same relative path for that package.
50*6c119a46SAndroid Build Coastguard Worker    #
51*6c119a46SAndroid Build Coastguard Worker    # These conflcits are left to the user of this `gensrcs` rule to resolve for
52*6c119a46SAndroid Build Coastguard Worker    # their use case, as at least Bazel will raise an error when they occur.
53*6c119a46SAndroid Build Coastguard Worker
54*6c119a46SAndroid Build Coastguard Worker    # Try to obtain the path to the package that defines `src_file`. It may or
55*6c119a46SAndroid Build Coastguard Worker    # may not be defined by the same package this `gensrcs` rule is in.
56*6c119a46SAndroid Build Coastguard Worker    # The `owner` label `package` attribute value is the closest we can get
57*6c119a46SAndroid Build Coastguard Worker    # to that path, but it may not be correct in all cases, such as if the
58*6c119a46SAndroid Build Coastguard Worker    # source path is itself for a generated file, where the generated file is
59*6c119a46SAndroid Build Coastguard Worker    # under a build artifact path, and not in the source tree.
60*6c119a46SAndroid Build Coastguard Worker    pkg_dirname = paths.dirname(src_file.short_path)
61*6c119a46SAndroid Build Coastguard Worker    rel_dirname = pkg_dirname
62*6c119a46SAndroid Build Coastguard Worker    if (src_file.is_source and src_file.owner and
63*6c119a46SAndroid Build Coastguard Worker        src_file.short_path.startswith(src_file.owner.package + "/")):
64*6c119a46SAndroid Build Coastguard Worker        rel_dirname = paths.dirname(paths.relativize(
65*6c119a46SAndroid Build Coastguard Worker            src_file.short_path,
66*6c119a46SAndroid Build Coastguard Worker            src_file.owner.package,
67*6c119a46SAndroid Build Coastguard Worker        ))
68*6c119a46SAndroid Build Coastguard Worker
69*6c119a46SAndroid Build Coastguard Worker    base_inc_ext = src_file.basename
70*6c119a46SAndroid Build Coastguard Worker    base_exc_ext = _remove_extension(base_inc_ext)
71*6c119a46SAndroid Build Coastguard Worker    rel_path_base_inc_ext = paths.join(rel_dirname, base_inc_ext)
72*6c119a46SAndroid Build Coastguard Worker    rel_path_base_exc_ext = paths.join(rel_dirname, base_exc_ext)
73*6c119a46SAndroid Build Coastguard Worker    pkg_path_base_inc_ext = paths.join(pkg_dirname, base_inc_ext)
74*6c119a46SAndroid Build Coastguard Worker    pkg_path_base_exc_ext = paths.join(pkg_dirname, base_exc_ext)
75*6c119a46SAndroid Build Coastguard Worker
76*6c119a46SAndroid Build Coastguard Worker    # Expand the output template
77*6c119a46SAndroid Build Coastguard Worker    return ctx.attr.output \
78*6c119a46SAndroid Build Coastguard Worker        .replace("$(SRC:PKG/PATH/BASE.EXT)", pkg_path_base_inc_ext) \
79*6c119a46SAndroid Build Coastguard Worker        .replace("$(SRC:PKG/PATH/BASE)", pkg_path_base_exc_ext) \
80*6c119a46SAndroid Build Coastguard Worker        .replace("$(SRC:PATH/BASE.EXT)", rel_path_base_inc_ext) \
81*6c119a46SAndroid Build Coastguard Worker        .replace("$(SRC:PATH/BASE)", rel_path_base_exc_ext) \
82*6c119a46SAndroid Build Coastguard Worker        .replace("$(SRC:BASE.EXT)", base_inc_ext) \
83*6c119a46SAndroid Build Coastguard Worker        .replace("$(SRC:BASE)", base_exc_ext) \
84*6c119a46SAndroid Build Coastguard Worker        .replace("$(SRC)", rel_path_base_inc_ext)
85*6c119a46SAndroid Build Coastguard Worker
86*6c119a46SAndroid Build Coastguard Worker# A rule to generate files based on provided srcs and tools.
87*6c119a46SAndroid Build Coastguard Workerdef _gensrcs_impl(ctx):
88*6c119a46SAndroid Build Coastguard Worker    # The next two assignments can be created by using ctx.resolve_command.
89*6c119a46SAndroid Build Coastguard Worker    # TODO: Switch to using ctx.resolve_command when it is out of
90*6c119a46SAndroid Build Coastguard Worker    # experimental.
91*6c119a46SAndroid Build Coastguard Worker    command = ctx.expand_location(ctx.attr.cmd)
92*6c119a46SAndroid Build Coastguard Worker    tools = [
93*6c119a46SAndroid Build Coastguard Worker        tool[DefaultInfo].files_to_run
94*6c119a46SAndroid Build Coastguard Worker        for tool in ctx.attr.tools
95*6c119a46SAndroid Build Coastguard Worker    ]
96*6c119a46SAndroid Build Coastguard Worker
97*6c119a46SAndroid Build Coastguard Worker    # Expand the shell command by substituting $(RULEDIR), which will be
98*6c119a46SAndroid Build Coastguard Worker    # the same for any source file.
99*6c119a46SAndroid Build Coastguard Worker    command = command.replace(
100*6c119a46SAndroid Build Coastguard Worker        "$(RULEDIR)",
101*6c119a46SAndroid Build Coastguard Worker        paths.join(
102*6c119a46SAndroid Build Coastguard Worker            ctx.var["GENDIR"],
103*6c119a46SAndroid Build Coastguard Worker            ctx.label.package,
104*6c119a46SAndroid Build Coastguard Worker        ),
105*6c119a46SAndroid Build Coastguard Worker    )
106*6c119a46SAndroid Build Coastguard Worker
107*6c119a46SAndroid Build Coastguard Worker    src_files = ctx.files.srcs
108*6c119a46SAndroid Build Coastguard Worker    out_files = []
109*6c119a46SAndroid Build Coastguard Worker    for src_file in src_files:
110*6c119a46SAndroid Build Coastguard Worker        # Expand the output path template for this source file.
111*6c119a46SAndroid Build Coastguard Worker        out_file_path = _expand_out_path_template(ctx, src_file)
112*6c119a46SAndroid Build Coastguard Worker
113*6c119a46SAndroid Build Coastguard Worker        # out_file is at output_file_path that is relative to
114*6c119a46SAndroid Build Coastguard Worker        # <GENDIR>/<gensrc-package-dir>, hence, the fullpath to out_file is
115*6c119a46SAndroid Build Coastguard Worker        # <GENDIR>/<gensrc-package-dir>/<out_file_path>
116*6c119a46SAndroid Build Coastguard Worker        out_file = ctx.actions.declare_file(out_file_path)
117*6c119a46SAndroid Build Coastguard Worker
118*6c119a46SAndroid Build Coastguard Worker        # Expand the command template for this source file by performing
119*6c119a46SAndroid Build Coastguard Worker        # substitution for $(SRC) and $(OUT).
120*6c119a46SAndroid Build Coastguard Worker        shell_command = command \
121*6c119a46SAndroid Build Coastguard Worker            .replace("$(SRC)", src_file.path) \
122*6c119a46SAndroid Build Coastguard Worker            .replace("$(OUT)", out_file.path)
123*6c119a46SAndroid Build Coastguard Worker
124*6c119a46SAndroid Build Coastguard Worker        # Run the shell comand to generate the output from the input.
125*6c119a46SAndroid Build Coastguard Worker        ctx.actions.run_shell(
126*6c119a46SAndroid Build Coastguard Worker            tools = tools,
127*6c119a46SAndroid Build Coastguard Worker            outputs = [out_file],
128*6c119a46SAndroid Build Coastguard Worker            inputs = [src_file],
129*6c119a46SAndroid Build Coastguard Worker            command = shell_command,
130*6c119a46SAndroid Build Coastguard Worker            progress_message = "Generating %s from %s" % (
131*6c119a46SAndroid Build Coastguard Worker                out_file.path,
132*6c119a46SAndroid Build Coastguard Worker                src_file.path,
133*6c119a46SAndroid Build Coastguard Worker            ),
134*6c119a46SAndroid Build Coastguard Worker        )
135*6c119a46SAndroid Build Coastguard Worker        out_files.append(out_file)
136*6c119a46SAndroid Build Coastguard Worker
137*6c119a46SAndroid Build Coastguard Worker    return [DefaultInfo(
138*6c119a46SAndroid Build Coastguard Worker        files = depset(out_files),
139*6c119a46SAndroid Build Coastguard Worker    )]
140*6c119a46SAndroid Build Coastguard Worker
141*6c119a46SAndroid Build Coastguard Workergensrcs = rule(
142*6c119a46SAndroid Build Coastguard Worker    implementation = _gensrcs_impl,
143*6c119a46SAndroid Build Coastguard Worker    doc = "This rule generates files, where each of the `srcs` files is " +
144*6c119a46SAndroid Build Coastguard Worker          "passed to `cmd` to generate an `output`.",
145*6c119a46SAndroid Build Coastguard Worker    attrs = {
146*6c119a46SAndroid Build Coastguard Worker        "srcs": attr.label_list(
147*6c119a46SAndroid Build Coastguard Worker            # We allow srcs to directly reference files, instead of only
148*6c119a46SAndroid Build Coastguard Worker            # allowing references to other rules such as filegroups.
149*6c119a46SAndroid Build Coastguard Worker            allow_files = True,
150*6c119a46SAndroid Build Coastguard Worker            # An empty srcs is likely an mistake.
151*6c119a46SAndroid Build Coastguard Worker            allow_empty = False,
152*6c119a46SAndroid Build Coastguard Worker            # srcs must be explicitly specified.
153*6c119a46SAndroid Build Coastguard Worker            mandatory = True,
154*6c119a46SAndroid Build Coastguard Worker            doc = "A list of source files to process",
155*6c119a46SAndroid Build Coastguard Worker        ),
156*6c119a46SAndroid Build Coastguard Worker        "output": attr.string(
157*6c119a46SAndroid Build Coastguard Worker            # By default we generate an output filename based on the input
158*6c119a46SAndroid Build Coastguard Worker            # filename (no extension).
159*6c119a46SAndroid Build Coastguard Worker            default = "$(SRC)",
160*6c119a46SAndroid Build Coastguard Worker            doc = "An output path template which is expanded to generate " +
161*6c119a46SAndroid Build Coastguard Worker                  "the output path given an source file. Portions " +
162*6c119a46SAndroid Build Coastguard Worker                  "of the source filename can be included in the expansion " +
163*6c119a46SAndroid Build Coastguard Worker                  "with one of: $(SRC:BASE), $(SRC:BASE.EXT), " +
164*6c119a46SAndroid Build Coastguard Worker                  "$(SRC:PATH/BASE), $(SRC:PATH/BASE), " +
165*6c119a46SAndroid Build Coastguard Worker                  "$(SRC:PKG/PATH/BASE), or $(SRC:PKG/PATH/BASE.ext). For " +
166*6c119a46SAndroid Build Coastguard Worker                  "example, specifying `output = " +
167*6c119a46SAndroid Build Coastguard Worker                  "\"includes/lib/$(SRC:BASE).h\"` would mean the input " +
168*6c119a46SAndroid Build Coastguard Worker                  "file `some_path/to/a.txt` generates `includes/lib/a.h`, " +
169*6c119a46SAndroid Build Coastguard Worker                  "while instead specifying `output = " +
170*6c119a46SAndroid Build Coastguard Worker                  "\"includes/lib/$(SRC:PATH/BASE.EXT).h\"` would expand " +
171*6c119a46SAndroid Build Coastguard Worker                  "to `includes/lib/some_path/to/a.txt.h`.",
172*6c119a46SAndroid Build Coastguard Worker        ),
173*6c119a46SAndroid Build Coastguard Worker        "cmd": attr.string(
174*6c119a46SAndroid Build Coastguard Worker            # cmd must be explicitly specified.
175*6c119a46SAndroid Build Coastguard Worker            mandatory = True,
176*6c119a46SAndroid Build Coastguard Worker            doc = "The command to run. Subject to $(location) expansion. " +
177*6c119a46SAndroid Build Coastguard Worker                  "$(SRC) represents each input file provided in `srcs` " +
178*6c119a46SAndroid Build Coastguard Worker                  "while $(OUT) reprensents corresponding output file " +
179*6c119a46SAndroid Build Coastguard Worker                  "generated by the rule. $(RULEDIR) is intepreted the same " +
180*6c119a46SAndroid Build Coastguard Worker                  "as it is in genrule.",
181*6c119a46SAndroid Build Coastguard Worker        ),
182*6c119a46SAndroid Build Coastguard Worker        "tools": attr.label_list(
183*6c119a46SAndroid Build Coastguard Worker            # We allow tools to directly reference files, as there could be a local script
184*6c119a46SAndroid Build Coastguard Worker            # used as a tool.
185*6c119a46SAndroid Build Coastguard Worker            allow_files = True,
186*6c119a46SAndroid Build Coastguard Worker            doc = "A list of tool dependencies for this rule. " +
187*6c119a46SAndroid Build Coastguard Worker                  "The path of an individual `tools` target //x:y can be " +
188*6c119a46SAndroid Build Coastguard Worker                  "obtained using `$(location //x:y)`",
189*6c119a46SAndroid Build Coastguard Worker            cfg = "exec",
190*6c119a46SAndroid Build Coastguard Worker        ),
191*6c119a46SAndroid Build Coastguard Worker    },
192*6c119a46SAndroid Build Coastguard Worker)
193