| --- |
| layout: documentation |
| title: Skylark Rules |
| --- |
| # Rules |
| |
| **Status: Experimental**. We may make breaking changes to the API, but we will |
| help you update your code. |
| |
| ## Rule creation |
| |
| In a Skylark extension, use the [rule](lib/globals.html#rule) function |
| to create a new rule and store it in a global variable. |
| [See example](cookbook.md#empty). |
| |
| A custom rule can be used just like a native rule. It has a mandatory `name` |
| attribute, you can refer to it with a label, and you can see it in |
| `bazel query`. |
| |
| The rule is analyzed when you explictly build it, or if it is a dependency of |
| the build. In this case, Bazel will execute its `implementation` function. This |
| function decides what the outputs of the rule are and how to build them (using |
| `actions`). During analysis, no external command can be executed: actions will |
| be run in the execution phase. |
| |
| ## Attributes |
| |
| An attribute is a rule argument, such as `srcs` or `deps`. You must list |
| the attributes and their types when you define a rule. |
| |
| ```python |
| sum = rule( |
| implementation=impl, |
| attrs={ |
| "number": attr.int(default=1), |
| "deps": attr.label_list(), |
| }, |
| ) |
| ``` |
| |
| The following attributes are implicitly added to every rule: `deprecation`, |
| `features`, `name`, `tags`, `testonly`, `visibility`. Test rules also have the |
| following attributes: `args`, `flaky`, `local`, `shard_count`, `size`, `timeout`. |
| |
| To access an attribute, use `ctx.attr.<attribute_name>`. The name and the |
| package of a rule are available with `ctx.label.name` and `ctx.label.package`. |
| |
| [See example.](cookbook.md#attr) |
| |
| ### <a name="private-attributes"></a> Private Attributes |
| |
| If an attribute name starts with `_` it is private and users cannot set it. It |
| is useful in particular for label attributes (your rule will have an |
| implicit dependency on this label). |
| |
| ```python |
| metal_compile = rule( |
| implementation=impl, |
| attrs={ |
| "srcs": attr.label_list(), |
| "_compiler": attr.label( |
| default=Label("//tools:metalc"), |
| single_file=True, |
| executable=True, |
| ), |
| }, |
| ) |
| ``` |
| |
| ## Implementation function |
| |
| Every rule requires an `implementation` function. It contains the actual |
| logic of the rule and is executed strictly in the Analysis Phase. The function |
| has exactly one input parameter, `ctx`, and it may return |
| the [runfiles](#runfiles) and [providers](#providers) |
| of the rule. The input parameter `ctx` can be used to access attribute values, |
| outputs and dependent targets, and files. It also has some helper functions. |
| See [the library](lib/ctx.html) for more context. Example: |
| |
| ```python |
| def impl(ctx): |
| ... |
| return struct( |
| runfiles=..., |
| my_provider=..., |
| ... |
| ) |
| |
| my_rule = rule( |
| implementation=impl, |
| ... |
| ) |
| ``` |
| |
| ## Files |
| |
| There are two kinds of files: files stored in the file system and generated |
| files. For each generated file, there must be one and only one generating |
| action, and each action must generate one or more output files. Bazel will throw |
| an error otherwise. |
| |
| ## Targets |
| |
| Every build rule corresponds to exactly one target. A target can create |
| [actions](#actions), can have dependencies (which can be files or |
| other build rules), [output files](#output-files) (generated by |
| its actions), and [providers](#providers). |
| |
| A target `y` depends on target `x` if `y` has a label or label list type |
| attribute where `x` is declared: |
| |
| ```python |
| my_rule( |
| name = "x", |
| ) |
| |
| my_rule( |
| name = "y", |
| deps = [":x"], |
| ) |
| ``` |
| |
| In the above case, it's possible to access targets declared in `my_rule.deps`: |
| |
| ```python |
| def impl(ctx): |
| for dep in ctx.attr.deps: |
| # Do something with dep |
| ... |
| |
| my_rule = rule( |
| implementation=impl, |
| attrs={ |
| "deps": attr.label_list(), |
| }, |
| ... |
| ) |
| ``` |
| |
| ## <a name="output-files"></a> Output files |
| |
| A target can declare output files, which must be generated by the target's |
| actions. There are three ways to create output files in Skylark: |
| |
| * If the rule is marked `executable`, it creates an output file of the same name |
| as the rule's. [See example](cookbook.md#outputs-executable) |
| |
| * The rule can declare default `outputs`, which are always generated. |
| [See example](cookbook.md#outputs-default) |
| |
| * The rule can have output or output list type attributes. In that case the |
| output files come from the actual attribute values. |
| [See example](cookbook.md#outputs-custom) |
| |
| All output files must have exactly one generating action. See the |
| [library](lib/ctx.html#outputs) for more context. |
| |
| ## Default outputs |
| |
| Every rule has a set of default outputs. This is used: |
| |
| * When the user runs `bazel build` on your target. Bazel will build the default |
| outputs of the rule. |
| |
| * When the target is used as a dependency of another rule. A rule can access |
| the default outputs by using [target.files](lib/Target.html#files). |
| This is the case, for example, if you use a rule in the `srcs` attribute of a |
| `genrule`. |
| |
| Use the `files` provider to specify the default outputs of a rule. |
| If left unspecified, it will contain all the declared outputs. |
| |
| ```python |
| def _impl(ctx): |
| # ... |
| return struct(files=set([file1, file2])) |
| ``` |
| |
| This can be useful for exposing files generated with |
| [ctx.new_file](lib/ctx.html#new_file). You can also have "implicit |
| outputs", i.e., files that are declared in the rule, but not in the default |
| outputs (like `_deploy.jar` in `java_binary`). |
| |
| ## Actions |
| |
| There are three ways to create actions: |
| |
| * [ctx.action](lib/ctx.html#action), to run a command. |
| * [ctx.file_action](lib/ctx.html#file_action), to write a string to a file. |
| * [ctx.template_action](lib/ctx.html#template_action), to generate a file from a template. |
| |
| Actions take a set (which can be empty) of input files and generate a (non-empty) |
| set of output files. |
| The set of input and output files must be known during the analysis phase. It |
| might depend on the value of attributes and information from dependencies, but |
| it cannot depend on the result of the execution. For example, if your action |
| runs the unzip command, you must specify which files you expect to be inflated |
| (before running unzip). |
| |
| Actions are comparable to pure functions: They should depend only on the |
| provided inputs, and avoid accessing computer information, username, clock, |
| network, or I/O devices (except for reading inputs and writing outputs). |
| |
| **If an action generates a file that is not listed in its outputs**: This is |
| fine, but the file will be ignored and cannot be used by other rules. |
| |
| **If an action does not generate a file that is listed in its outputs**: This is |
| an execution error and the build will fail. This happens for instance when a |
| compilation fails. |
| |
| **If an action generates an unknown number of outputs and you want to keep them |
| all**, you may group them in a zip file. This way, you will be able to declare |
| your output. |
| |
| **If an action does not list a file it uses as an input**, the action execution |
| will most likely result in an error. The file is not guaranteed to be available |
| to the action, so if it **is** there, it's due to coincidence or error. |
| |
| **If an action lists a file as an input, but does not use it**: This is fine. |
| However, it can affect action execution order, resulting in sub-optimal |
| performance. |
| |
| Dependencies are resolved by Bazel, which will decide which actions are |
| executed. It is an error if there is a cycle in the dependency graph. Creating |
| an action does not guarantee that it will be executed: It depends on whether |
| its outputs are needed for the build. |
| |
| ## Configurations |
| |
| By default, a target is built in the target configuration. For each label |
| attribute, you can decide whether the dependency should be built in the same |
| configuration, or in the host configuration. |
| |
| In general, sources, dependent libraries, and executables that will be needed at |
| runtime can use the same configuration. |
| |
| Tools that are executed as part of the build (e.g., compilers, code generators) |
| should be built for the host configuration. In this case, specify `cfg=HOST_CFG` |
| in the attribute. |
| |
| `DATA_CFG` is present for legacy reasons and should be used for `data` |
| attributes. |
| |
| |
| ## <a name="fragments"></a> Configuration Fragments |
| |
| Rules may access configuration fragments such as `cpp`, `java` and `jvm`. |
| However, all required fragments must be declared in order to avoid access |
| errors: |
| |
| ```python |
| def impl(ctx): |
| # Using ctx.fragments.cpp would lead to an error since it was not declared. |
| x = ctx.fragments.java |
| ... |
| |
| my_rule = rule( |
| implementation=impl, |
| fragments=["java"], # Required fragments of the target configuration |
| host_fragments=["java"], # Required fragments of the host configuration |
| ... |
| ) |
| ``` |
| |
| `ctx.fragments` only provides configuration fragments for the target |
| configuration. If you want to access fragments for the host configuration, |
| use `ctx.host_fragments` instead. |
| |
| ## Providers |
| |
| Providers are used to access information from other rules. A rule depending on |
| another rule has access to the data the latter provides. These data can be e.g. |
| output files, the libraries the dependent rule is using to link or compile, or |
| anything the depending rule should know about. Using providers is the only way |
| to exchange data between rules. |
| |
| A rule can only access data provided by its direct dependencies, not that of |
| transitive dependencies: if rule `top` depends on `middle`, and `middle` depends |
| on `bottom`, then `middle` is a direct dependency of `top` and `bottom` is a |
| transitive dependency of `top`. In this scenario `top` can only access data |
| provided by `middle`. If `middle` also provides the data that `bottom` provided |
| to it, then and only then can `top` access it. |
| |
| The following data types can be passed using providers: |
| |
| * `bool` |
| * `integer` |
| * `string` |
| * `file` |
| * `label` |
| * `None` |
| * anything composed of these types and `lists`, `dicts`, `sets` or `structs` |
| |
| Providers are created from the return value of the rule implementation function: |
| |
| ```python |
| def rule_implementation(ctx): |
| ... |
| return struct( |
| transitive_data=set(["a", "b", "c"]) |
| ) |
| ``` |
| |
| A dependent rule might access these data as struct fields of the `target` being |
| dependened upon: |
| |
| ```python |
| def dependent_rule_implementation(ctx): |
| ... |
| s = set() |
| for dep_target in ctx.attr.deps: |
| # Use `print(dir(dep_target))` to see the list of providers. |
| s += dep_target.transitive_data |
| ... |
| ``` |
| |
| Providers are only available during the analysis phase. Examples of usage: |
| |
| * [mandatory providers](cookbook.md#mandatory-providers) |
| * [optional providers](cookbook.md#optional-providers) |
| |
| ## Runfiles |
| |
| Runfiles are a set of files used by the (often executable) output of a rule |
| during runtime (as opposed to build time, i.e. when the binary itself is |
| generated). |
| During execution, Bazel creates a directory tree containing symlinks pointing to |
| the runfiles, staging the environment for the binary so it can access the |
| runfiles during runtime. |
| |
| Runfiles can be added manually during rule creation and/or collected |
| transitively from the rule's dependencies: |
| |
| ```python |
| def rule_implementation(ctx): |
| ... |
| transitive_runfiles = set() |
| for dep in ctx.attr.special_dependencies: |
| transitive_runfiles += dep.transitive_runtime_files |
| |
| runfiles = ctx.runfiles( |
| # Add some files manually. |
| files=[ctx.file.some_data_file], |
| # Add transitive files from dependencies manually. |
| transitive_files=transitive_runfiles, |
| # Collect runfiles from the common locations: transitively from srcs, |
| # deps and data attributes. |
| collect_default=True, |
| ) |
| # Add a field named "runfiles" to the return struct in order to actually |
| # create the symlink tree. |
| return struct(runfiles=runfiles) |
| ``` |
| |
| Note that non-executable rule outputs can also have runfiles. For example, a |
| library might need some external files during runtime, and every dependent |
| binary should know about them. |
| |
| Also note that if an action uses an executable, the executable's runfiles can |
| be used when the action executes. |
| |
| Normally, the relative path of a file in the runfiles tree is the same as the |
| relative path of that file in the source tree or generated output tree. If these |
| need to be different for some reason, you can specify the `root_symlinks` or |
| `symlinks` arguments. The `root_symlinks` is a dictionary mapping paths to |
| files, where the paths are relative to the root of the runfiles directory. The |
| `symlinks` dictionary is the same, but paths are implicitly prefixed with the |
| name of the workspace. |
| |
| ```python |
| ... |
| runfiles = ctx.runfiles( |
| root_symlinks={"some/path/here.foo": ctx.file.some_data_file2} |
| symlinks={"some/path/here.bar": ctx.file.some_data_file3} |
| ) |
| # Creates something like: |
| # sometarget.runfiles/ |
| # some/ |
| # path/ |
| # here.foo -> some_data_file2 |
| # <workspace_name>/ |
| # some/ |
| # path/ |
| # here.bar -> some_data_file3 |
| ``` |
| |
| If `symlinks` or `root_symlinks` is used, be careful not to map two different |
| files to the same path in the runfiles tree. This will cause the build to fail |
| with an error describing the conflict. To fix, you will need to modify your |
| `ctx.runfiles` arguments to remove the collision. This checking will be done for |
| any targets using your rule, as well as targets of any kind that depend on those |
| targets. |
| |
| ## Instrumented files |
| |
| Instrumented files are a set of files used by the coverage command. A rule can |
| use the `instrumented_files` provider to provide information about which files |
| should be used for measuring coverage. |
| |
| ```python |
| def rule_implementation(ctx): |
| ... |
| return struct(instrumented_files=struct( |
| # Optional: File extensions used to filter files from source_attributes. |
| # If not provided, then all files from source_attributes will be |
| # added to instrumented files, if an empty list is provided, then |
| # no files from source attributes will be added. |
| extensions=["ext1", "ext2"], |
| # Optional: Attributes that contain source files for this rule. |
| source_attributes=["srcs"], |
| # Optional: Attributes for dependencies that could include instrumented |
| # files. |
| dependency_attributes=["data", "deps"])) |
| ``` |
| |
| ## Executable rules |
| |
| An executable rule is a rule that users can run using `bazel run`. |
| |
| To make a rule executable, set `executable=True` in the |
| [rule function](lib/globals.html#rule). During the analysis |
| phase, the rule must generate the output file `ctx.outputs.executable`. |
| [See example](cookbook.md#outputs-executable) |
| |
| ## Test rules |
| |
| Test rules are run using `bazel test`. |
| |
| To create a test rule, set `test=True` in the |
| [rule function](lib/globals.html#rule). The name of the rule must |
| also end with `_test`. Test rules are implicitly executable, which means they |
| must generate the output file `ctx.outputs.executable`. |
| |
| Test rules inherit the following attributes: `args`, `flaky`, `local`, |
| `shard_count`, `size`, `timeout`. |