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