diff --git a/rules/README.md b/rules/README.md index 40d67c44a..986b2a2ee 100644 --- a/rules/README.md +++ b/rules/README.md @@ -24,6 +24,12 @@ passing information from a dependency to a target. * [default outputs](default_outputs/): Example of a rule with a declared output. +* [mandatory provider](mandatory_provider/): Example with a mandatory provider, + to access information from a dependency. + +* [optional provider](optional_provider/): Example with an optional provider, + to access information from a dependency. + * [depsets](depsets/): Example of a using a depset to gather transitive information. Each target collects data from its dependencies. @@ -36,6 +42,9 @@ control on the behavior of the rules. * [test rule](test_rule/): Example of a test rule. +* [runfiles](runfiles/): Example of an executable rule with runfiles (files + required at runtime). + * [computed dependencies](computed_dependencies/): Example with computed dependencies. The set of implicit dependencies depends on the rule attributes. diff --git a/rules/attributes/printer.bzl b/rules/attributes/printer.bzl index 71321909f..72ebd58ad 100644 --- a/rules/attributes/printer.bzl +++ b/rules/attributes/printer.bzl @@ -10,6 +10,10 @@ def _impl(ctx): # A label can represent any number of files (possibly 0). print(" files = " + str([f.path for f in d.files.to_list()])) + # For debugging, consider using `dir` to explore the existing fields. + print(dir(ctx)) # prints all the fields and methods of ctx + print(dir(ctx.attr)) # prints all the attributes of the rule + printer = rule( implementation=_impl, attrs={ diff --git a/rules/mandatory_provider/BUILD b/rules/mandatory_provider/BUILD new file mode 100644 index 000000000..1e1ab5712 --- /dev/null +++ b/rules/mandatory_provider/BUILD @@ -0,0 +1,19 @@ +load(":sum.bzl", "sum") + +sum( + name = "n", + deps = [ + ":n2", + ":n5", + ], +) + +sum( + name = "n2", + number = 2, +) + +sum( + name = "n5", + number = 5, +) diff --git a/rules/mandatory_provider/sum.bzl b/rules/mandatory_provider/sum.bzl new file mode 100644 index 000000000..ca046fbc4 --- /dev/null +++ b/rules/mandatory_provider/sum.bzl @@ -0,0 +1,28 @@ +"""Rule with a mandatory provider. + +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. +""" + +NumberInfo = provider("number") + +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"} +) diff --git a/rules/optional_provider/BUILD b/rules/optional_provider/BUILD new file mode 100644 index 000000000..1e1ab5712 --- /dev/null +++ b/rules/optional_provider/BUILD @@ -0,0 +1,19 @@ +load(":sum.bzl", "sum") + +sum( + name = "n", + deps = [ + ":n2", + ":n5", + ], +) + +sum( + name = "n2", + number = 2, +) + +sum( + name = "n5", + number = 5, +) diff --git a/rules/optional_provider/sum.bzl b/rules/optional_provider/sum.bzl new file mode 100644 index 000000000..01a883870 --- /dev/null +++ b/rules/optional_provider/sum.bzl @@ -0,0 +1,28 @@ +"""Rule with an optional provider. + +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. Dependencies are not required to provide a number. +""" + +NumberInfo = provider("number") + +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"} +) diff --git a/rules/runfiles/BUILD b/rules/runfiles/BUILD new file mode 100644 index 000000000..4cd34de24 --- /dev/null +++ b/rules/runfiles/BUILD @@ -0,0 +1,12 @@ +load(":execute.bzl", "execute") + +# `bazel build //runfiles:all` will create an executable. +# `bazel run //runfiles:all` will run it. +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"], +) diff --git a/rules/runfiles/data.txt b/rules/runfiles/data.txt new file mode 100644 index 000000000..cd0875583 --- /dev/null +++ b/rules/runfiles/data.txt @@ -0,0 +1 @@ +Hello world! diff --git a/rules/runfiles/execute.bzl b/rules/runfiles/execute.bzl new file mode 100644 index 000000000..f5037de86 --- /dev/null +++ b/rules/runfiles/execute.bzl @@ -0,0 +1,34 @@ +"""Create an executable with runfiles. + +Runfiles are files that are needed at runtime (when the executable in run). +This example also shows a use of `ctx.expand_location`. +""" + +def _impl(ctx): + # 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(ctx.attr.command, ctx.attr.data) + + # Create the output executable file with command as its content. + ctx.file_action( + output=ctx.outputs.executable, + content=command, + executable=True) + + # 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. + return [DefaultInfo( + runfiles=ctx.runfiles(files=ctx.files.data) + )] + +execute = rule( + implementation=_impl, + executable=True, + attrs={ + "command": attr.string(), + "data": attr.label_list(allow_files=True), + }, +)