From 59a0b3f7c9d69b7be3be23ee57a8c058130ac9e8 Mon Sep 17 00:00:00 2001 From: laurentlb Date: Wed, 25 Jul 2018 13:36:31 -0700 Subject: Remove cookbook The cookbook was not maintained and was replaced by https://github.com/bazelbuild/examples RELNOTES: None. PiperOrigin-RevId: 206046316 --- site/docs/skylark/cookbook.md | 958 ------------------------------------------ site/docs/skylark/macros.md | 8 - 2 files changed, 966 deletions(-) delete mode 100644 site/docs/skylark/cookbook.md (limited to 'site') diff --git a/site/docs/skylark/cookbook.md b/site/docs/skylark/cookbook.md deleted file mode 100644 index d382db065b..0000000000 --- a/site/docs/skylark/cookbook.md +++ /dev/null @@ -1,958 +0,0 @@ ---- -layout: documentation -title: Extension Examples ---- - -# Extension Examples - -**Tip:** For further examples, see the [GitHub repository](https://github.com/bazelbuild/examples/tree/master/rules). - - - -## Macro creating a rule - -An example of a macro creating a rule. - -`empty.bzl`: - -```python -def _impl(ctx): - print("This rule does nothing") - -empty = rule(implementation=_impl) -``` - -`extension.bzl`: - -```python -# Loading the rule. The rule doesn't have to be in a separate file. -load("//pkg:empty.bzl", "empty") - -def macro(name, visibility=None): - # Creating the rule. - empty(name = name, visibility = visibility) -``` - -`BUILD`: - -```python -load("//pkg:extension.bzl", "macro") - -macro(name = "myrule") -``` - -## Macro creating a native rule - -An example of a macro creating a native rule. Native rules are special rules -that are automatically available (without load). They are -accessed using the native module. - -`extension.bzl`: - -```python -def macro(name, visibility=None): - # Creating a native genrule. - native.genrule( - name = name, - outs = [name + ".txt"], - cmd = "echo hello > $@", - visibility = visibility, - ) -``` - -`BUILD`: - -```python -load("//pkg:extension.bzl", "macro") - -macro(name = "myrule") -``` - -## Macro multiple rules - -There's currently no easy way to create a rule that directly uses the -action of a native rule. You can work around this using macros: - -```python -def _impl(ctx): - return struct([...], - # When instrumenting this rule, again hide implementation from - # users. - instrumented_files( - source_attributes = ["srcs", "csrcs"], - dependency_attributes = ["deps", "cdeps"])) - -# This rule is private and can only be accessed from the current file. -_cc_and_something_else_binary = rule(implementation=_impl) - - -# This macro is public, it's the public interface to instantiate the rule. -def cc_and_something_else_binary(name, srcs, deps, csrcs, cdeps): - cc_binary_name = "%s.cc_binary" % name - - native.cc_binary( - name = cc_binary_name, - srcs = csrcs, - deps = cdeps, - visibility = ["//visibility:private"] - ) - - _cc_and_something_else_binary( - name = name, - srcs = srcs, - deps = deps, - # A label attribute so that this depends on the internal rule. - cc_binary = cc_binary_name, - # Redundant labels attributes so that the rule with this target name knows - # about everything it would know about if cc_and_something_else_binary - # were an actual rule instead of a macro. - csrcs = csrcs, - cdeps = cdeps) -``` - - -## Conditional instantiation - -Macros can look at previously instantiated rules. This is done with -`native.existing_rule`, which returns information on a single rule defined in -the same `BUILD` file, eg., - -```python -native.existing_rule("descriptor_proto") -``` - -This is useful to avoid instantiating the same rule twice, which is an -error. For example, the following macro will simulate a test suite, -instantiating tests for diverse flavors of the same test. - -`extension.bzl`: - -```python -def system_test(name, test_file, flavor): - n = "system_test_%s_%s_test" % (test_file, flavor) - if native.existing_rule(n) == None: - native.py_test( - name = n, - srcs = [ - "test_driver.py", - test_file, - ], - args = ["--flavor=" + flavor], - ) - return n - -def system_test_suite(name, flavors=["default"], test_files=[]): - ts = [] - for flavor in flavors: - for test in test_files: - ts.append(system_test(name, test, flavor)) - native.test_suite(name = name, tests = ts) -``` - -In the following BUILD file, note how `(basic_test.py, fast)` is emitted for -both the `smoke` test suite and the `thorough` test suite. - -`BUILD`: - -```python -load("//pkg:extension.bzl", "system_test_suite") - -# Run all files through the 'fast' flavor. -system_test_suite( - name = "smoke", - flavors = ["fast"], - test_files = glob(["*_test.py"]), -) - -# Run the basic test through all flavors. -system_test_suite( - name = "thorough", - flavors = [ - "fast", - "debug", - "opt", - ], - test_files = ["basic_test.py"], -) -``` - - -## Aggregating over the BUILD file - -Macros can collect information from the BUILD file as processed so far. We call -this aggregation. The typical example is collecting data from all rules of a -certain kind. This is done by calling -native.existing\_rules, which -returns a dictionary representing all rules defined so far in the current BUILD -file. The dictionary has entries of the form `name` => `rule`, with the values -using the same format as `native.existing_rule`. - -```python -def archive_cc_src_files(tag): - """Create an archive of all C++ sources that have the given tag.""" - all_src = [] - for r in native.existing_rules().values(): - if tag in r["tags"] and r["kind"] == "cc_library": - all_src.append(r["srcs"]) - native.genrule(cmd = "zip $@ $^", srcs = all_src, outs = ["out.zip"]) -``` - -Since `native.existing_rules` constructs a potentially large dictionary, you -should avoid calling it repeatedly within BUILD file. - -## Empty rule - -Minimalist example of a rule that does nothing. If you build it, the target will -succeed (with no generated file). - -[See example on GitHub](https://github.com/bazelbuild/examples/tree/master/rules/empty) - -`empty.bzl`: - -```python -def _impl(ctx): - # You may use print for debugging. - print("This rule does nothing") - -empty = rule(implementation=_impl) -``` - -`BUILD`: - -```python -load("//pkg:empty.bzl", "empty") - -empty(name = "nothing") -``` - -## Rule with attributes - -Example of a rule that shows how to declare attributes and access them. - -[See example on GitHub](https://github.com/bazelbuild/examples/tree/master/rules/attributes) - -`printer.bzl`: - -```python -def _impl(ctx): - # You may use print for debugging. - print("Rule name = %s, package = %s" % (ctx.label.name, ctx.label.package)) - - # This prints the labels of the deps attribute. - print("There are %d deps" % len(ctx.attr.deps)) - for i in ctx.attr.deps: - print("- %s" % i.label) - # A label can represent any number of files (possibly 0). - print(" files = %s" % [f.path for f in i.files]) - -printer = rule( - implementation=_impl, - attrs={ - # Do not declare "name": It is added automatically. - "number": attr.int(default = 1), - "deps": attr.label_list(allow_files=True), - }) -``` - -`BUILD`: - -```python -load("//pkg:printer.bzl", "printer") - -printer( - name = "nothing", - deps = [ - "BUILD", - ":other", - ], -) - -printer(name = "other") -``` - -If you execute this file, some information is printed as a warning by the -rule. No file is generated. - -## Simple shell command - -Example of a rule that runs a shell command on an input file specified by -the user. The output has the same name as the rule, with a `.size` suffix. - -While convenient, Shell commands should be used carefully. Generating the -command-line can lead to escaping and injection issues. It can also create -portability problems. It is often better to declare a binary target in a -BUILD file and execute it. - -See the example [executing a binary](#execute-bin). - -`size.bzl`: - -```python -def _impl(ctx): - output = ctx.outputs.out - input = ctx.file.file - # The command may only access files declared in inputs. - ctx.actions.run_shell( - inputs=[input], - outputs=[output], - progress_message="Getting size of %s" % input.short_path, - command="stat -L -c%%s %s > %s" % (input.path, output.path)) - -size = rule( - implementation=_impl, - attrs={"file": attr.label(mandatory=True, allow_files=True, single_file=True)}, - outputs={"out": "%{name}.size"}, -) -``` - -`foo.txt`: - -``` -Hello -``` - -`BUILD`: - -```python -load("//pkg:size.bzl", "size") - -size( - name = "foo_size", - file = "foo.txt", -) -``` - -## Write string to a file - -Example of a rule that writes a string to a file. - -`file.bzl`: - -```python -def _impl(ctx): - output = ctx.outputs.out - ctx.file_action(output=output, content=ctx.attr.content) - -file = rule( - implementation=_impl, - attrs={"content": attr.string()}, - outputs={"out": "%{name}.txt"}, -) -``` - -`BUILD`: - -```python -load("//pkg:file.bzl", "file") - -file( - name = "hello", - content = "Hello world", -) -``` - -## Execute a binary - -This rule executes an existing binary. In this particular example, the -binary is a tool that merges files. During the analysis phase, we cannot -access any arbitrary label: the dependency must have been previously -declared. To do so, the rule needs a label attribute. In this example, we -will give the label a default value and make it private (so that it is not -visible to end users). Keeping the label private can simplify maintenance, -since you can easily change the arguments and flags you pass to the tool. - -`execute.bzl`: - -```python -def _impl(ctx): - # The list of arguments we pass to the script. - args = [ctx.outputs.out.path] + [f.path for f in ctx.files.srcs] - # Action to call the script. - ctx.actions.run( - inputs=ctx.files.srcs, - outputs=[ctx.outputs.out], - arguments=args, - progress_message="Merging into %s" % ctx.outputs.out.short_path, - executable=ctx.executable._merge_tool) - -concat = rule( - implementation=_impl, - attrs={ - "srcs": attr.label_list(allow_files=True), - "out": attr.output(mandatory=True), - "_merge_tool": attr.label(executable=True, cfg="host", allow_files=True, - default=Label("//pkg:merge")) - } -) -``` - -Any executable target can be used. In this example, we will use a -`sh_binary` rule that concatenates all the inputs. - -`BUILD`: - -``` -load("execute", "concat") - -concat( - name = "sh", - srcs = [ - "header.html", - "body.html", - "footer.html", - ], - out = "page.html", -) - -# This target is used by the shell rule. -sh_binary( - name = "merge", - srcs = ["merge.sh"], -) -``` - -`merge.sh`: - -```sh -#!/bin/sh - -out=$1 -shift -cat "$@" > $out -``` - -`header.html`: - -``` - -``` - -`body.html`: - -``` -content -``` - -`footer.html`: - -``` - -``` - -## Execute an input binary - -This rule has a mandatory `binary` attribute. It is a label that can refer -only to executable rules or files. - -`execute.bzl`: - -```python -def _impl(ctx): - # ctx.actions.declare_file is used for temporary files. - f = ctx.actions.declare_file(ctx.configuration.bin_dir, "hello") - # As with outputs, each time you declare a file, - # you need an action to generate it. - ctx.actions.write(output=f, content=ctx.attr.input_content) - - ctx.actions.run( - inputs=[f], - outputs=[ctx.outputs.out], - executable=ctx.executable.binary, - progress_message="Executing %s" % ctx.executable.binary.short_path, - arguments=[ - f.path, - ctx.outputs.out.path, # Access the output file using - # ctx.outputs. - ] - ) - -execute = rule( - implementation=_impl, - attrs={ - "binary": attr.label(cfg="host", mandatory=True, allow_files=True, - executable=True), - "input_content": attr.string(), - "out": attr.output(mandatory=True), - }, -) -``` - -`a.sh`: - -```bash -#!/bin/bash - -tr 'a-z' 'A-Z' < $1 > $2 -``` - -`BUILD`: - -```python -load("//pkg:execute.bzl", "execute") - -execute( - name = "e", - input_content = "some text", - binary = "a.sh", - out = "foo", -) -``` - -## Runfiles and location substitution - -`execute.bzl`: - -```python -def _impl(ctx): - executable = ctx.outputs.executable - command = ctx.attr.command - # Expand the label in the command string to a runfiles-relative path. - # The second arg is the list of labels that may be expanded. - command = ctx.expand_location(command, ctx.attr.data) - # Create the output executable file with command as its content. - ctx.file_action( - output=executable, - content=command, - executable=True) - - return [DefaultInfo( - # Create runfiles from the files specified in the data attribute. - # The shell executable - the output of this rule - can use them at - # runtime. It is also possible to define data_runfiles and - # default_runfiles. However if runfiles is specified it's not possible to - # define the above ones since runfiles sets them both. - # Remember, that the struct returned by the implementation function needs - # to have a field named "runfiles" in order to create the actual runfiles - # symlink tree. - runfiles=ctx.runfiles(files=ctx.files.data) - )] - -execute = rule( - implementation=_impl, - executable=True, - attrs={ - "command": attr.string(), - "data": attr.label_list(allow_files=True), - }, -) -``` - -`data.txt`: - -``` -Hello World! -``` - -`BUILD`: - -```python -load("//pkg:execute.bzl", "execute") - -execute( - name = "e", - # The location will be expanded to "pkg/data.txt", and it will reference - # the data.txt file in runfiles when this target is invoked as - # "bazel run //pkg:e". - command = "cat $(location :data.txt)", - data = [":data.txt"] -) -``` - -## Computed dependencies - -Bazel needs to know about all dependencies before doing the analysis phase and -calling the implementation function. Dependencies can be computed based on the -rule attributes: to do so, use a function as the default -value of an attribute (the attribute must be private and have type `label` or -`list of labels`). The parameters of this function must correspond to the -attributes that are accessed in the function body. - -The example below computes the md5 sum of a file. The file can be preprocessed -using a filter. The exact dependencies depend on the filter chosen by the user. - -[See example on GitHub](https://github.com/bazelbuild/examples/tree/master/rules/computed_dependencies) - -`hash.bzl`: - -```python -_filters = { - "comments": Label("//pkg:comments"), - "spaces": Label("//pkg:spaces"), - "none": None, -} - -def _get_filter(filter): # requires attribute "filter" - # Return the value for the attribute "_filter_bin" - # It can be a label or None. - return _filters[filter] - -def _impl(ctx): - src = ctx.file.src - - if not ctx.attr._filter_bin: - # Skip the processing - processed = src - else: - processed = ctx.actions.declare_file(ctx.label.name + "_processed") - # Run the selected binary - ctx.actions.run( - outputs = [processed], - inputs = [ctx.file.src], - progress_message="Apply filter '%s'" % ctx.attr.filter, - arguments = [ctx.file.src.path, processed.path], - executable = ctx.executable._filter_bin) - - # Compute the hash - out = ctx.outputs.text - ctx.actions.run( - outputs = [out], - inputs = [processed], - command = "md5sum < %s > %s" % (processed.path, out.path)) - -md5_sum = rule( - implementation=_impl, - attrs={ - "filter": attr.string(values=_filters.keys(), default="none"), - "src": attr.label(mandatory=True, single_file=True, allow_files=True), - "_filter_bin": attr.label(default=_get_filter, executable=True), - }, - outputs = {"text": "%{name}.txt"}) -``` - -`BUILD`: - -```python -load("//pkg:hash.bzl", "md5_sum") - -md5_sum( - name = "hash", - src = "hello.txt", - filter = "spaces", -) - -sh_binary( - name = "comments", - srcs = ["comments.sh"], -) - -sh_binary( - name = "spaces", - srcs = ["spaces.sh"], -) -``` - -`hello.txt`: - -``` -Hello World! -``` - -`comments.sh`: - -``` -#!/bin/bash -grep -v '^ *#' $1 > $2 # Remove lines with only a Python-style comment -``` - -`spaces.sh`: - -``` -#!/bin/bash -tr -d ' ' < $1 > $2 # Remove spaces -``` - -## Mandatory providers - -In this example, rules have a `number` attribute. Each rule adds its -number with the numbers of its transitive dependencies, and write the -result in a file. This shows how to transfer information from a dependency -to its dependents. - -`sum.bzl`: - -```python -NumberInfo = provider() - -def _impl(ctx): - result = ctx.attr.number - for dep in ctx.attr.deps: - result += dep[NumberInfo].number - ctx.file_action(output=ctx.outputs.out, content=str(result)) - - # Return the provider with result, visible to other rules. - return [NumberInfo(number=result)] - -sum = rule( - implementation=_impl, - attrs={ - "number": attr.int(default=1), - # All deps must provide all listed providers. - "deps": attr.label_list(providers=[NumberInfo]), - }, - outputs = {"out": "%{name}.sum"} -) -``` - -`BUILD`: - -```python -load("//pkg:sum.bzl", "sum") - -sum( - name = "n", - deps = ["n2", "n5"], -) - -sum( - name = "n2", - number = 2, -) - -sum( - name = "n5", - number = 5, -) -``` - -## Optional providers - -This is a similar example, but dependencies may not provide a number. - -`sum.bzl`: - -```python -NumberInfo = provider() - -def _impl(ctx): - result = ctx.attr.number - for dep in ctx.attr.deps: - if NumberInfo in dep: - result += dep[NumberInfo].number - ctx.file_action(output=ctx.outputs.out, content=str(result)) - - # Return the provider with result, visible to other rules. - return [NumberInfo(number=result)] - -sum = rule( - implementation=_impl, - attrs={ - "number": attr.int(default=1), - "deps": attr.label_list(), - }, - outputs = {"out": "%{name}.sum"} -) -``` - -`BUILD`: - -```python -load("//pkg:sum.bzl", "sum") - -sum( - name = "n", - deps = ["n2", "n5"], -) - -sum( - name = "n2", - number = 2, -) - -sum( - name = "n5", - number = 5, -) -``` - -## Default executable output - -This example shows how to create a default executable output. - -[See example on GitHub](https://github.com/bazelbuild/examples/tree/master/rules/executable) - -`extension.bzl`: - -```python -def _impl(ctx): - ctx.file_action( - # Access the executable output file using ctx.outputs.executable. - output=ctx.outputs.executable, - content="#!/bin/bash\necho Hello!", - executable=True - ) - # The executable output is added automatically to this target. - -executable_rule = rule( - implementation=_impl, - executable=True -) -``` - -`BUILD`: - -```python -load("//pkg:extension.bzl", "executable_rule") - -executable_rule(name = "my_rule") -``` - -## Default outputs - -This example shows how to create default outputs for a rule. - -`extension.bzl`: - -```python -def _impl(ctx): - ctx.file_action( - # Access the default outputs using ctx.outputs.. - output=ctx.outputs.my_output, - content="Hello World!" - ) - # The default outputs are added automatically to this target. - -rule_with_outputs = rule( - implementation=_impl, - outputs = { - # %{name} is substituted with the rule's name - "my_output": "%{name}.txt" - } -) -``` - -`BUILD`: - -```python -load("//pkg:extension.bzl", "rule_with_outputs") - -rule_with_outputs(name = "my_rule") -``` - -## Custom outputs - -This example shows how to create custom (user defined) outputs for a rule. -This rule takes a list of output file name templates from the user and -creates each of them containing a "Hello World!" message. - -`extension.bzl`: - -```python -def _impl(ctx): - # Access the custom outputs using ctx.outputs.. - for output in ctx.outputs.outs: - ctx.file_action( - output=output, - content="Hello World!" - ) - # The custom outputs are added automatically to this target. - -rule_with_outputs = rule( - implementation=_impl, - attrs={ - "outs": attr.output_list() - } -) -``` - -`BUILD`: - -```python -load("//pkg:extension.bzl", "rule_with_outputs") - -rule_with_outputs( - name = "my_rule", - outs = ["my_output.txt"] -) -``` - -## Master rules - -This example shows how to create master rules to bind other rules together. The -code below uses genrules for simplicity, but this technique is more useful with -other rules. For example, if you need to compile C++ files, you can reuse -`cc_library`. - -`extension.bzl`: - -```python -def _impl(ctx): - # Aggregate the output files from the depending rules - files = depset() - files += ctx.attr.dep_rule_1.files - files += ctx.attr.dep_rule_2.files - return [DefaultInfo(files=files)] - -# This rule binds the depending rules together -master_rule = rule( - implementation=_impl, - attrs={ - "dep_rule_1": attr.label(), - "dep_rule_2": attr.label() - } -) - -def macro(name, cmd, input): - # Create the depending rules - name_1 = name + "_dep_1" - name_2 = name + "_dep_2" - native.genrule( - name = name_1, - cmd = cmd, - outs = [name_1 + ".txt"] - ) - native.genrule( - name = name_2, - cmd = "echo " + input + " >$@", - outs = [name_2 + ".txt"] - ) - # Create the master rule - master_rule( - name = name, - dep_rule_1 = ":" + name_1, - dep_rule_2 = ":" + name_2 - ) -``` - -`BUILD`: - -```python -load("//pkg:extension.bzl", "macro") - -# This creates the target :my_rule -macro( - name = "my_rule", - cmd = "echo something > $@", - input = "Hello World" -) -``` - -## Debugging tips - -Here are some examples on how to debug macros and rules using -print. - -`debug.bzl`: - -```python -print("print something when the module is loaded") - -def _impl(ctx): - print("print something when the rule implementation is executed") - print(type("abc")) # prints string, the type of "abc" - print(dir(ctx)) # prints all the fields and methods of ctx - print(dir(ctx.attr)) # prints all the attributes of the rule - # prints the objects each separated with new line - print("object1", "object2", sep="\n") - -debug = rule(implementation=_impl) -``` - -`BUILD`: - -```python -load("//pkg:debug.bzl", "debug") - -debug( - name = "printing_rule" -) -``` diff --git a/site/docs/skylark/macros.md b/site/docs/skylark/macros.md index 035aae1862..d91bb7eb63 100644 --- a/site/docs/skylark/macros.md +++ b/site/docs/skylark/macros.md @@ -29,14 +29,6 @@ def my_macro(name, visibility=None): If you need to know the package name (i.e. which BUILD file is calling the macro), use the function [native.package_name()](lib/native.html#package_name). -## Examples - -* [Macro creating rules](cookbook.md#macro). - -* [Macro creating native rules](cookbook.md#macro_native). - -* [Macro combining multiple rules](cookbook.md#macro_compound). - ## Debugging * `bazel query --output=build //my/path:all` will show you how the BUILD file -- cgit v1.2.3