diff options
Diffstat (limited to 'site/docs/skylark/rules.md')
-rw-r--r-- | site/docs/skylark/rules.md | 460 |
1 files changed, 2 insertions, 458 deletions
diff --git a/site/docs/skylark/rules.md b/site/docs/skylark/rules.md index 809b8763da..22b62057eb 100644 --- a/site/docs/skylark/rules.md +++ b/site/docs/skylark/rules.md @@ -1,460 +1,4 @@ --- -layout: documentation -title: Rules +layout: redirect +redirect: docs/skylark/rules.html --- -# Rules - -**Status: Experimental**. We may make breaking changes to the API, but we will - help you update your code. - -A rule defines a series of actions that Bazel should perform on inputs to get a -set of outputs. For example, a C++ binary rule might take a set of .cpp -files (the inputs), run `g++` on them (the action), and return an executable -file (the output). - -Note that, from Bazel's perspective, `g++` and the standard C++ libraries are -also inputs to this rule. As a rule writer, you must consider not only the -user-provided inputs to a rule, but also all of the tools and libraries required -to execute the actions (called _implicit inputs_). - -## Rule creation - -In a `.bzl` file, use the [rule](lib/globals.html#rule) -function to create a new rule and store it in a global variable: - -```python -my_rule = rule(...) -``` - -See [the cookbook](cookbook.md#empty) for examples. The rule can then be -loaded by BUILD files: - -```python -load('//some/pkg:whatever.bzl', 'my_rule') -``` - -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 explicitly 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`. - -Labels listed in `attr` will be inputs to the rule. - -To access an attribute in a rule's implementation, use -`ctx.attr.<attribute_name>`. The name and the package of a rule are available -with `ctx.label.name` and `ctx.label.package`. - -See [an example](cookbook.md#attr) of using `attr` in a rule. - -### <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"), - allow_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: - -* 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) - -Each output file 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 must group them in a single file (e.g., a zip, tar, or other -archive format). This way, you will be able to deterministically declare your -outputs. - -**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"` -in the attribute. - -The configuration `"data"` is present for legacy reasons and should be used for -the `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 -depended 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`. |