| Rules |
| ===== |
| |
| **Status: Experimental** |
| |
| [TOC] |
| |
| Rule creation |
| ------------- |
| |
| In a Skylark extension, use the `rule` function to create a new rule and store |
| it in a global variable. [See example](cookbook.md#empty). |
| |
| Attributes |
| ---------- |
| |
| An attribute is a rule argument, such as `srcs` or `deps`. You have to list |
| the attributes and their type when you define a rule. |
| |
| ```python |
| sum = rule( |
| implementation=impl, |
| attr = { |
| "number": attr.int(default = 1), |
| "deps": attr.label_list(), |
| }, |
| ) |
| ``` |
| |
| If an attribute 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). |
| |
| [See example.](cookbook.md#attr) |
| |
| The rule implementation function |
| -------------------------------- |
| |
| Every rule has to have an implementation function. This 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](library.html#ctx) 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 ore more output files. Otherwise |
| Bazel will throw an error. |
| |
| 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` is depending 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.targets.deps: |
| # Do something with dep |
| ... |
| |
| my_rule = rule( |
| implementation=impl, |
| attrs = { |
| "deps": attr.label_list() |
| } |
| ... |
| ) |
| ``` |
| |
| Output files {#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](library.html#modules.ctx.outputs) for more context. |
| |
| |
| Actions |
| ------- |
| |
| There are three ways to create actions: |
| |
| * `ctx.action` |
| * `ctx.file_action` |
| * `ctx.template_action` |
| |
| Actions take a set (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 zip command, you must specify which files you expect (before running |
| zip). |
| |
| 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 a command generates a file that is not listed in the outputs**: It is fine. |
| The file will be ignored and cannot be used by other rules. |
| |
| **If a command does not generate a file that is listed in the outputs**: It is an |
| execution error and the build will fail. This happens for instance when a |
| compilation fails. |
| |
| **If a command 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 a command 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 a coincidence or error. |
| |
| **If a command lists a file as an input, but does not use it**: It is fine. However |
| it can affect the 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. |
| |
| Providers |
| --------- |
| |
| Providers are used to access information from another rule. 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. |
| |
| Only the following data types are allowed to pass 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 dependent_rule_implementation(ctx): |
| ... |
| return struct( |
| transitive_data = set(["a", "b", "c"]) |
| ) |
| ``` |
| |
| A depending rule might access these data as struct fields of the depending |
| `target`: |
| |
| ```python |
| def depending_rule_implementation(ctx): |
| ... |
| s = set() |
| for dep_target in ctx.targets.deps: |
| 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). |
| Bazel creates a directory tree containing symlinks pointing to the |
| runfiles during execution, to stage this environment for the binary which can |
| thus access them during runtime. |
| |
| Runfiles can be added manually during rule creation and/or collected |
| transitively from dependent rules: |
| |
| ```python |
| def rule_implementation(ctx): |
| ... |
| transitive_runfiles = set() |
| for dep in ctx.targets.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 depending |
| binary should know about it. |
| |
| Also note that if an action uses an executable, the executable's runfiles can |
| be used when the action executes. |