1# Copyright (C) 2022 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:shell.bzl", "shell") 16load(":exec_aspect.bzl", "ExecAspectInfo", "exec_aspect") 17 18_DEFAULT_HASHBANG = "/bin/bash -e" 19 20def _impl(ctx): 21 out_file = ctx.actions.declare_file(ctx.label.name) 22 23 for target in ctx.attr.data: 24 if ExecAspectInfo not in target: 25 continue 26 if target[ExecAspectInfo].args: 27 fail("{}: {} must not have args. Use embedded_exec to wrap it.".format(ctx.label, target.label)) 28 if target[ExecAspectInfo].env: 29 fail("{}: {} must not have env. Use embedded_exec to wrap it.".format(ctx.label, target.label)) 30 31 content = "#!{}\n".format(ctx.attr.hashbang) 32 content += ctx.attr.script 33 34 content = ctx.expand_location(content, ctx.attr.data) 35 ctx.actions.write(out_file, content, is_executable = True) 36 37 runfiles = ctx.runfiles(files = ctx.files.data + [out_file]) 38 runfiles = runfiles.merge_all([target[DefaultInfo].default_runfiles for target in ctx.attr.data]) 39 40 return DefaultInfo( 41 files = depset([out_file]), 42 executable = out_file, 43 runfiles = runfiles, 44 ) 45 46exec = rule( 47 implementation = _impl, 48 doc = """Run a script when `bazel run` this target. 49 50See [documentation] for the `args` attribute. 51""", 52 attrs = { 53 "data": attr.label_list(aspects = [exec_aspect], allow_files = True, doc = """A list of labels providing runfiles. Labels may be used in `script`. 54 55Executables in `data` must not have the `args` and `env` attribute. Use 56[`embedded_exec`](#embedded_exec) to wrap the depended target so its env and args 57are preserved. 58"""), 59 "hashbang": attr.string(default = _DEFAULT_HASHBANG, doc = "Hashbang of the script."), 60 "script": attr.string(doc = """The script. 61 62Use `$(rootpath <label>)` to refer to the path of a target specified in `data`. See 63[documentation](https://bazel.build/reference/be/make-variables#predefined_label_variables). 64 65Use `$@` to refer to the args attribute of this target. 66 67See `build/bazel_common_rules/exec/tests/BUILD` for examples. 68"""), 69 }, 70 executable = True, 71) 72 73exec_test = rule( 74 implementation = _impl, 75 doc = """Run a test script when `bazel test` this target. 76 77See [documentation] for the `args` attribute. 78""", 79 attrs = { 80 "data": attr.label_list(aspects = [exec_aspect], allow_files = True, doc = """A list of labels providing runfiles. Labels may be used in `script`. 81 82Executables in `data` must not have the `args` and `env` attribute. Use 83[`embedded_exec`](#embedded_exec) to wrap the depended target so its env and args 84are preserved. 85"""), 86 "hashbang": attr.string(default = _DEFAULT_HASHBANG, doc = "Hashbang of the script."), 87 "script": attr.string(doc = """The script. 88 89Use `$(rootpath <label>)` to refer to the path of a target specified in `data`. See 90[documentation](https://bazel.build/reference/be/make-variables#predefined_label_variables). 91 92Use `$@` to refer to the args attribute of this target. 93 94See `build/bazel_common_rules/exec/tests/BUILD` for examples. 95"""), 96 }, 97 test = True, 98) 99 100def exec_rule( 101 cfg = None, 102 attrs = None): 103 """Returns a rule() that is similar to `exec`, but with the given incoming transition. 104 105 Args: 106 cfg: [Incoming edge transition](https://bazel.build/extending/config#incoming-edge-transitions) 107 on the rule 108 attrs: Additional attributes to be added to the rule. 109 110 Specify `_allowlist_function_transition` if you need a transition. 111 """ 112 113 fixed_attrs = { 114 "data": attr.label_list(aspects = [exec_aspect], allow_files = True), 115 "hashbang": attr.string(default = _DEFAULT_HASHBANG), 116 "script": attr.string(), 117 } 118 119 if attrs == None: 120 attrs = {} 121 attrs = attrs | fixed_attrs 122 123 return rule( 124 implementation = _impl, 125 attrs = attrs, 126 cfg = cfg, 127 executable = True, 128 ) 129